mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-08-07 19:08:32 -05:00
Compare commits
61 Commits
feature-sa
...
more-info-
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1b6a9d3816 | ||
![]() |
07c298523a | ||
![]() |
0ea159683d | ||
![]() |
f0b6e79d14 | ||
![]() |
302cb22ec6 | ||
![]() |
4210addb46 | ||
![]() |
06746b4b31 | ||
![]() |
2f5533a179 | ||
![]() |
2f267341f8 | ||
![]() |
88befee527 | ||
![]() |
c4a7186cd2 | ||
![]() |
d974f092aa | ||
![]() |
23501b9060 | ||
![]() |
f09965464a | ||
![]() |
ae5bd2d2fd | ||
![]() |
2b73007e7e | ||
![]() |
8505fa3e54 | ||
![]() |
a51093afc2 | ||
![]() |
2fdae59288 | ||
![]() |
4637f5c5e5 | ||
![]() |
5e7ee924ff | ||
![]() |
fded55dc70 | ||
![]() |
20da51278e | ||
![]() |
293c84d871 | ||
![]() |
1fe8599266 | ||
![]() |
5410074062 | ||
![]() |
4b8f6ed643 | ||
![]() |
f8689c4819 | ||
![]() |
cebc227701 | ||
![]() |
814df94e8d | ||
![]() |
fa496dfc8d | ||
![]() |
924471b59c | ||
![]() |
feb320cae9 | ||
![]() |
9178af5fb2 | ||
![]() |
850444c2fc | ||
![]() |
90baba2cec | ||
![]() |
9889c59d3d | ||
![]() |
3d2a3ede71 | ||
![]() |
bc019fab96 | ||
![]() |
1cd21d0f38 | ||
![]() |
f940ed0b7b | ||
![]() |
3180ccf4cb | ||
![]() |
43abb0541b | ||
![]() |
a3a405354f | ||
![]() |
09e98d600e | ||
![]() |
01a39b9bb4 | ||
![]() |
3b0b40f071 | ||
![]() |
6dce83865f | ||
![]() |
18252a19d7 | ||
![]() |
733a9674d6 | ||
![]() |
f3b6e15321 | ||
![]() |
6591d5da63 | ||
![]() |
c974dc9400 | ||
![]() |
1671d49d44 | ||
![]() |
6b248ef140 | ||
![]() |
735681d294 | ||
![]() |
a9085c65c5 | ||
![]() |
e312425b1c | ||
![]() |
13fe064f6e | ||
![]() |
958f98d7e5 | ||
![]() |
dfad3c4d8e |
@@ -20,7 +20,6 @@
|
|||||||
#
|
#
|
||||||
# This file is intended only to be used through VSCOde devcontainers. See README.md
|
# This file is intended only to be used through VSCOde devcontainers. See README.md
|
||||||
# in the folder .devcontainer.
|
# in the folder .devcontainer.
|
||||||
|
|
||||||
services:
|
services:
|
||||||
broker:
|
broker:
|
||||||
image: docker.io/library/redis:7
|
image: docker.io/library/redis:7
|
||||||
|
1
.github/dependabot.yml
vendored
1
.github/dependabot.yml
vendored
@@ -1,6 +1,5 @@
|
|||||||
# Please see the documentation for all configuration options:
|
# Please see the documentation for all configuration options:
|
||||||
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||||
|
|
||||||
version: 2
|
version: 2
|
||||||
# Required for uv support for now
|
# Required for uv support for now
|
||||||
enable-beta-ecosystems: true
|
enable-beta-ecosystems: true
|
||||||
|
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -12,7 +12,7 @@ on:
|
|||||||
branches-ignore:
|
branches-ignore:
|
||||||
- 'translations**'
|
- 'translations**'
|
||||||
env:
|
env:
|
||||||
DEFAULT_UV_VERSION: "0.7.x"
|
DEFAULT_UV_VERSION: "0.8.x"
|
||||||
# This is the default version of Python to use in most steps which aren't specific
|
# This is the default version of Python to use in most steps which aren't specific
|
||||||
DEFAULT_PYTHON_VERSION: "3.11"
|
DEFAULT_PYTHON_VERSION: "3.11"
|
||||||
jobs:
|
jobs:
|
||||||
|
1
.github/workflows/cleanup-tags.yml
vendored
1
.github/workflows/cleanup-tags.yml
vendored
@@ -4,7 +4,6 @@
|
|||||||
# Requires a PAT with the correct scope set in the secrets.
|
# Requires a PAT with the correct scope set in the secrets.
|
||||||
#
|
#
|
||||||
# This workflow will not trigger runs on forked repos.
|
# This workflow will not trigger runs on forked repos.
|
||||||
|
|
||||||
name: Cleanup Image Tags
|
name: Cleanup Image Tags
|
||||||
on:
|
on:
|
||||||
delete:
|
delete:
|
||||||
|
11
.github/workflows/repo-maintenance.yml
vendored
11
.github/workflows/repo-maintenance.yml
vendored
@@ -19,12 +19,19 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
days-before-stale: 7
|
days-before-stale: 7
|
||||||
days-before-close: 14
|
days-before-close: 14
|
||||||
any-of-labels: 'stale,cant-reproduce,not a bug'
|
any-of-issue-labels: 'cant-reproduce,not a bug'
|
||||||
stale-issue-label: stale
|
stale-issue-label: stale
|
||||||
stale-pr-label: stale
|
|
||||||
stale-issue-message: >
|
stale-issue-message: >
|
||||||
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. See our [contributing guidelines](https://github.com/paperless-ngx/paperless-ngx/blob/dev/CONTRIBUTING.md#automatic-repository-maintenance) for more details.
|
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. See our [contributing guidelines](https://github.com/paperless-ngx/paperless-ngx/blob/dev/CONTRIBUTING.md#automatic-repository-maintenance) for more details.
|
||||||
|
|
||||||
|
days-before-pr-stale: 14
|
||||||
|
days-before-pr-close: 7
|
||||||
|
stale-pr-message: ""
|
||||||
|
stale-pr-label: stale
|
||||||
|
exempt-pr-labels: 'notable'
|
||||||
|
close-pr-message: >
|
||||||
|
This pull request has been automatically closed because it has not had recent activity. Thank you for your contributions. Please open a new pull request or discussion if you would like to continue working on this change.
|
||||||
|
|
||||||
lock-threads:
|
lock-threads:
|
||||||
name: 'Lock Old Threads'
|
name: 'Lock Old Threads'
|
||||||
if: github.repository_owner == 'paperless-ngx'
|
if: github.repository_owner == 'paperless-ngx'
|
||||||
|
2
.github/workflows/translate-strings.yml
vendored
2
.github/workflows/translate-strings.yml
vendored
@@ -61,7 +61,7 @@ jobs:
|
|||||||
cd src-ui
|
cd src-ui
|
||||||
pnpm run ng extract-i18n
|
pnpm run ng extract-i18n
|
||||||
- name: Commit changes
|
- name: Commit changes
|
||||||
uses: stefanzweifel/git-auto-commit-action@v5
|
uses: stefanzweifel/git-auto-commit-action@v6
|
||||||
with:
|
with:
|
||||||
file_pattern: 'src-ui/messages.xlf src/locale/en_US/LC_MESSAGES/django.po'
|
file_pattern: 'src-ui/messages.xlf src/locale/en_US/LC_MESSAGES/django.po'
|
||||||
commit_message: "Auto translate strings"
|
commit_message: "Auto translate strings"
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
# This file configures pre-commit hooks.
|
# This file configures pre-commit hooks.
|
||||||
# See https://pre-commit.com/ for general information
|
# See https://pre-commit.com/ for general information
|
||||||
# See https://pre-commit.com/hooks.html for a listing of possible hooks
|
# See https://pre-commit.com/hooks.html for a listing of possible hooks
|
||||||
|
|
||||||
repos:
|
repos:
|
||||||
# General hooks
|
# General hooks
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
@@ -29,7 +28,7 @@ repos:
|
|||||||
- id: check-case-conflict
|
- id: check-case-conflict
|
||||||
- id: detect-private-key
|
- id: detect-private-key
|
||||||
- repo: https://github.com/codespell-project/codespell
|
- repo: https://github.com/codespell-project/codespell
|
||||||
rev: v2.4.0
|
rev: v2.4.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: codespell
|
- id: codespell
|
||||||
exclude: "(^src-ui/src/locale/)|(^src-ui/pnpm-lock.yaml)|(^src-ui/e2e/)|(^src/paperless_mail/tests/samples/)"
|
exclude: "(^src-ui/src/locale/)|(^src-ui/pnpm-lock.yaml)|(^src-ui/e2e/)|(^src/paperless_mail/tests/samples/)"
|
||||||
@@ -38,7 +37,7 @@ repos:
|
|||||||
- json
|
- json
|
||||||
# See https://github.com/prettier/prettier/issues/15742 for the fork reason
|
# See https://github.com/prettier/prettier/issues/15742 for the fork reason
|
||||||
- repo: https://github.com/rbubley/mirrors-prettier
|
- repo: https://github.com/rbubley/mirrors-prettier
|
||||||
rev: 'v3.3.3'
|
rev: 'v3.6.2'
|
||||||
hooks:
|
hooks:
|
||||||
- id: prettier
|
- id: prettier
|
||||||
types_or:
|
types_or:
|
||||||
@@ -50,17 +49,17 @@ repos:
|
|||||||
- 'prettier-plugin-organize-imports@4.1.0'
|
- 'prettier-plugin-organize-imports@4.1.0'
|
||||||
# Python hooks
|
# Python hooks
|
||||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
rev: v0.9.9
|
rev: v0.12.2
|
||||||
hooks:
|
hooks:
|
||||||
- id: ruff
|
- id: ruff
|
||||||
- id: ruff-format
|
- id: ruff-format
|
||||||
- repo: https://github.com/tox-dev/pyproject-fmt
|
- repo: https://github.com/tox-dev/pyproject-fmt
|
||||||
rev: "v2.5.1"
|
rev: "v2.6.0"
|
||||||
hooks:
|
hooks:
|
||||||
- id: pyproject-fmt
|
- id: pyproject-fmt
|
||||||
# Dockerfile hooks
|
# Dockerfile hooks
|
||||||
- repo: https://github.com/AleksaC/hadolint-py
|
- repo: https://github.com/AleksaC/hadolint-py
|
||||||
rev: v2.12.0.3
|
rev: v2.12.1b3
|
||||||
hooks:
|
hooks:
|
||||||
- id: hadolint
|
- id: hadolint
|
||||||
# Shell script hooks
|
# Shell script hooks
|
||||||
@@ -77,7 +76,7 @@ repos:
|
|||||||
hooks:
|
hooks:
|
||||||
- id: shellcheck
|
- id: shellcheck
|
||||||
- repo: https://github.com/google/yamlfmt
|
- repo: https://github.com/google/yamlfmt
|
||||||
rev: v0.14.0
|
rev: v0.17.2
|
||||||
hooks:
|
hooks:
|
||||||
- id: yamlfmt
|
- id: yamlfmt
|
||||||
exclude: "^src-ui/pnpm-lock.yaml"
|
exclude: "^src-ui/pnpm-lock.yaml"
|
||||||
|
@@ -141,7 +141,7 @@ The admins occasionally invite contributors directly if we believe having them o
|
|||||||
# Automatic Repository Maintenance
|
# Automatic Repository Maintenance
|
||||||
|
|
||||||
The Paperless-ngx team appreciates all effort and interest from the community in filing bug reports, creating feature requests, sharing ideas and helping other
|
The Paperless-ngx team appreciates all effort and interest from the community in filing bug reports, creating feature requests, sharing ideas and helping other
|
||||||
community members. That said, in an effort to keep the repository organized and managebale the project uses automatic handling of certain areas:
|
community members. That said, in an effort to keep the repository organized and manageable the project uses automatic handling of certain areas:
|
||||||
|
|
||||||
- Issues that cannot be reproduced will be marked 'stale' after 7 days of inactivity and closed after 14 further days of inactivity.
|
- Issues that cannot be reproduced will be marked 'stale' after 7 days of inactivity and closed after 14 further days of inactivity.
|
||||||
- Issues, pull requests and discussions that are closed will be locked after 30 days of inactivity.
|
- Issues, pull requests and discussions that are closed will be locked after 30 days of inactivity.
|
||||||
|
@@ -32,7 +32,7 @@ RUN set -eux \
|
|||||||
# Purpose: Installs s6-overlay and rootfs
|
# Purpose: Installs s6-overlay and rootfs
|
||||||
# Comments:
|
# Comments:
|
||||||
# - Don't leave anything extra in here either
|
# - Don't leave anything extra in here either
|
||||||
FROM ghcr.io/astral-sh/uv:0.7.9-python3.12-bookworm-slim AS s6-overlay-base
|
FROM ghcr.io/astral-sh/uv:0.8.4-python3.12-bookworm-slim AS s6-overlay-base
|
||||||
|
|
||||||
WORKDIR /usr/src/s6
|
WORKDIR /usr/src/s6
|
||||||
|
|
||||||
@@ -265,4 +265,4 @@ ENTRYPOINT ["/init"]
|
|||||||
|
|
||||||
EXPOSE 8000
|
EXPOSE 8000
|
||||||
|
|
||||||
HEALTHCHECK --interval=30s --timeout=10s --retries=5 CMD [ "curl", "-fs", "-S", "--max-time", "2", "http://localhost:8000" ]
|
HEALTHCHECK --interval=30s --timeout=10s --retries=5 CMD [ "curl", "-fs", "-S", "-L", "--max-time", "2", "http://localhost:8000" ]
|
||||||
|
@@ -1,8 +1,7 @@
|
|||||||
# Docker Compose file for running paperless testing with actual gotenberg
|
# Docker Compose file for running paperless testing with actual Gotenberg
|
||||||
# and Tika containers for a more end to end test of the Tika related functionality
|
# and Tika containers for a more end to end test of the Tika related functionality
|
||||||
# Can be used locally or by the CI to start the necessary containers with the
|
# Can be used locally or by the CI to start the necessary containers with the
|
||||||
# correct networking for the tests
|
# correct networking for the tests
|
||||||
|
|
||||||
services:
|
services:
|
||||||
gotenberg:
|
gotenberg:
|
||||||
image: docker.io/gotenberg/gotenberg:8.20
|
image: docker.io/gotenberg/gotenberg:8.20
|
||||||
|
@@ -32,6 +32,6 @@
|
|||||||
# Note that this is different from PAPERLESS_OCR_LANGUAGE (default=eng), which defines
|
# Note that this is different from PAPERLESS_OCR_LANGUAGE (default=eng), which defines
|
||||||
# the language used for OCR.
|
# the language used for OCR.
|
||||||
# The container installs English, German, Italian, Spanish and French by default.
|
# The container installs English, German, Italian, Spanish and French by default.
|
||||||
# See https://packages.debian.org/search?keywords=tesseract-ocr-&searchon=names&suite=buster
|
# See https://packages.debian.org/search?keywords=tesseract-ocr-&searchon=names
|
||||||
# for available languages.
|
# for available languages.
|
||||||
#PAPERLESS_OCR_LANGUAGES=tur ces
|
#PAPERLESS_OCR_LANGUAGES=tur ces
|
||||||
|
@@ -16,8 +16,8 @@
|
|||||||
# - Instead of SQLite (default), MariaDB is used as the database server.
|
# - Instead of SQLite (default), MariaDB is used as the database server.
|
||||||
# - Apache Tika and Gotenberg servers are started with paperless and paperless
|
# - Apache Tika and Gotenberg servers are started with paperless and paperless
|
||||||
# is configured to use these services. These provide support for consuming
|
# is configured to use these services. These provide support for consuming
|
||||||
# Office documents (Word, Excel, Power Point and their LibreOffice counter-
|
# Office documents (Word, Excel, PowerPoint and their LibreOffice counter-
|
||||||
# parts.
|
# parts).
|
||||||
#
|
#
|
||||||
# To install and update paperless with this file, do the following:
|
# To install and update paperless with this file, do the following:
|
||||||
#
|
#
|
||||||
@@ -25,11 +25,9 @@
|
|||||||
# and '.env' into a folder.
|
# and '.env' into a folder.
|
||||||
# - Run 'docker compose pull'.
|
# - Run 'docker compose pull'.
|
||||||
# - Run 'docker compose up -d'.
|
# - Run 'docker compose up -d'.
|
||||||
|
|
||||||
#
|
#
|
||||||
# For more extensive installation and update instructions, refer to the
|
# For more extensive installation and update instructions, refer to the
|
||||||
# documentation.
|
# documentation.
|
||||||
|
|
||||||
services:
|
services:
|
||||||
broker:
|
broker:
|
||||||
image: docker.io/library/redis:8
|
image: docker.io/library/redis:8
|
||||||
|
@@ -24,7 +24,6 @@
|
|||||||
#
|
#
|
||||||
# For more extensive installation and update instructions, refer to the
|
# For more extensive installation and update instructions, refer to the
|
||||||
# documentation.
|
# documentation.
|
||||||
|
|
||||||
services:
|
services:
|
||||||
broker:
|
broker:
|
||||||
image: docker.io/library/redis:8
|
image: docker.io/library/redis:8
|
||||||
|
@@ -25,7 +25,6 @@
|
|||||||
#
|
#
|
||||||
# For more extensive installation and update instructions, refer to the
|
# For more extensive installation and update instructions, refer to the
|
||||||
# documentation.
|
# documentation.
|
||||||
|
|
||||||
services:
|
services:
|
||||||
broker:
|
broker:
|
||||||
image: docker.io/library/redis:8
|
image: docker.io/library/redis:8
|
||||||
|
@@ -16,8 +16,8 @@
|
|||||||
# - Instead of SQLite (default), PostgreSQL is used as the database server.
|
# - Instead of SQLite (default), PostgreSQL is used as the database server.
|
||||||
# - Apache Tika and Gotenberg servers are started with paperless and paperless
|
# - Apache Tika and Gotenberg servers are started with paperless and paperless
|
||||||
# is configured to use these services. These provide support for consuming
|
# is configured to use these services. These provide support for consuming
|
||||||
# Office documents (Word, Excel, Power Point and their LibreOffice counter-
|
# Office documents (Word, Excel, PowerPoint and their LibreOffice counter-
|
||||||
# parts.
|
# parts).
|
||||||
#
|
#
|
||||||
# To install and update paperless with this file, do the following:
|
# To install and update paperless with this file, do the following:
|
||||||
#
|
#
|
||||||
@@ -28,7 +28,6 @@
|
|||||||
#
|
#
|
||||||
# For more extensive installation and update instructions, refer to the
|
# For more extensive installation and update instructions, refer to the
|
||||||
# documentation.
|
# documentation.
|
||||||
|
|
||||||
services:
|
services:
|
||||||
broker:
|
broker:
|
||||||
image: docker.io/library/redis:8
|
image: docker.io/library/redis:8
|
||||||
|
@@ -24,7 +24,6 @@
|
|||||||
#
|
#
|
||||||
# For more extensive installation and update instructions, refer to the
|
# For more extensive installation and update instructions, refer to the
|
||||||
# documentation.
|
# documentation.
|
||||||
|
|
||||||
services:
|
services:
|
||||||
broker:
|
broker:
|
||||||
image: docker.io/library/redis:8
|
image: docker.io/library/redis:8
|
||||||
|
@@ -16,8 +16,8 @@
|
|||||||
#
|
#
|
||||||
# - Apache Tika and Gotenberg servers are started with paperless and paperless
|
# - Apache Tika and Gotenberg servers are started with paperless and paperless
|
||||||
# is configured to use these services. These provide support for consuming
|
# is configured to use these services. These provide support for consuming
|
||||||
# Office documents (Word, Excel, Power Point and their LibreOffice counter-
|
# Office documents (Word, Excel, PowerPoint and their LibreOffice counter-
|
||||||
# parts.
|
# parts).
|
||||||
#
|
#
|
||||||
# To install and update paperless with this file, do the following:
|
# To install and update paperless with this file, do the following:
|
||||||
#
|
#
|
||||||
@@ -28,7 +28,6 @@
|
|||||||
#
|
#
|
||||||
# For more extensive installation and update instructions, refer to the
|
# For more extensive installation and update instructions, refer to the
|
||||||
# documentation.
|
# documentation.
|
||||||
|
|
||||||
services:
|
services:
|
||||||
broker:
|
broker:
|
||||||
image: docker.io/library/redis:8
|
image: docker.io/library/redis:8
|
||||||
|
@@ -21,7 +21,6 @@
|
|||||||
#
|
#
|
||||||
# For more extensive installation and update instructions, refer to the
|
# For more extensive installation and update instructions, refer to the
|
||||||
# documentation.
|
# documentation.
|
||||||
|
|
||||||
services:
|
services:
|
||||||
broker:
|
broker:
|
||||||
image: docker.io/library/redis:8
|
image: docker.io/library/redis:8
|
||||||
|
@@ -306,7 +306,7 @@ in dedicated folders according to their nature: `archive`, `originals`,
|
|||||||
If `-sm` or `--split-manifest` is provided, information about document
|
If `-sm` or `--split-manifest` is provided, information about document
|
||||||
will be placed in individual json files, instead of a single JSON file. The main
|
will be placed in individual json files, instead of a single JSON file. The main
|
||||||
manifest.json will still contain application wide information (e.g. tags, correspondent,
|
manifest.json will still contain application wide information (e.g. tags, correspondent,
|
||||||
documenttype, etc)
|
document type, etc)
|
||||||
|
|
||||||
If `-z` or `--zip` is provided, the export will be a zip file
|
If `-z` or `--zip` is provided, the export will be a zip file
|
||||||
in the target directory, named according to the current local date or the
|
in the target directory, named according to the current local date or the
|
||||||
@@ -457,6 +457,22 @@ of the index and usually makes queries faster and also ensures that the
|
|||||||
autocompletion works properly. This command is regularly invoked by the
|
autocompletion works properly. This command is regularly invoked by the
|
||||||
task scheduler.
|
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}
|
### Managing filenames {#renamer}
|
||||||
|
|
||||||
If you use paperless' feature to
|
If you use paperless' feature to
|
||||||
|
@@ -159,6 +159,58 @@ Available options are `postgresql` and `mariadb`.
|
|||||||
|
|
||||||
Defaults to unset, which uses Django’s built-in defaults.
|
Defaults to unset, which uses Django’s built-in defaults.
|
||||||
|
|
||||||
|
#### [`PAPERLESS_DB_POOLSIZE=<int>`](#PAPERLESS_DB_POOLSIZE) {#PAPERLESS_DB_POOLSIZE}
|
||||||
|
|
||||||
|
: Defines the maximum number of database connections to keep in the pool.
|
||||||
|
|
||||||
|
Only applies to PostgreSQL. This setting is ignored for other database engines.
|
||||||
|
|
||||||
|
The value must be greater than or equal to 1 to be used.
|
||||||
|
Defaults to unset, which disables connection pooling.
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
|
||||||
|
A small pool is typically sufficient — for example, a size of 4.
|
||||||
|
Make sure your PostgreSQL server's max_connections setting is large enough to handle:
|
||||||
|
```(Paperless workers + Celery workers) × pool size + safety margin```
|
||||||
|
For example, with 4 Paperless workers and 2 Celery workers, and a pool size of 4:
|
||||||
|
(4 + 2) × 4 + 10 = 34 connections required.
|
||||||
|
|
||||||
|
#### [`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
|
## Optional Services
|
||||||
|
|
||||||
### Tika {#tika}
|
### Tika {#tika}
|
||||||
@@ -968,6 +1020,22 @@ still perform some basic text pre-processing before matching.
|
|||||||
|
|
||||||
Defaults to 1.
|
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}
|
#### [`PAPERLESS_EMAIL_TASK_CRON=<cron expression>`](#PAPERLESS_EMAIL_TASK_CRON) {#PAPERLESS_EMAIL_TASK_CRON}
|
||||||
|
|
||||||
: Configures the scheduled email fetching frequency. The value
|
: Configures the scheduled email fetching frequency. The value
|
||||||
|
@@ -95,13 +95,13 @@ first-time setup.
|
|||||||
|
|
||||||
7. You can now either ...
|
7. You can now either ...
|
||||||
|
|
||||||
- install redis or
|
- install Redis or
|
||||||
|
|
||||||
- use the included `scripts/start_services.sh` to use docker to fire
|
- use the included `scripts/start_services.sh` to use Docker to fire
|
||||||
up a redis instance (and some other services such as tika,
|
up a Redis instance (and some other services such as Tika,
|
||||||
gotenberg and a database server) or
|
Gotenberg and a database server) or
|
||||||
|
|
||||||
- spin up a bare redis container
|
- spin up a bare Redis container
|
||||||
|
|
||||||
```
|
```
|
||||||
docker run -d -p 6379:6379 --restart unless-stopped redis:latest
|
docker run -d -p 6379:6379 --restart unless-stopped redis:latest
|
||||||
@@ -147,7 +147,7 @@ $ ng build --configuration production
|
|||||||
### Testing
|
### Testing
|
||||||
|
|
||||||
- Run `pytest` in the `src/` directory to execute all tests. This also
|
- Run `pytest` in the `src/` directory to execute all tests. This also
|
||||||
generates a HTML coverage report. When runnings test, `paperless.conf`
|
generates a HTML coverage report. When running tests, `paperless.conf`
|
||||||
is loaded as well. However, the tests rely on the default
|
is loaded as well. However, the tests rely on the default
|
||||||
configuration. This is not ideal. But for now, make sure no settings
|
configuration. This is not ideal. But for now, make sure no settings
|
||||||
except for DEBUG are overridden when testing.
|
except for DEBUG are overridden when testing.
|
||||||
|
@@ -30,7 +30,7 @@ physical documents into a searchable online archive so you can keep, well, _less
|
|||||||
- Utilizes the open-source Tesseract engine to recognize more than 100 languages.
|
- Utilizes the open-source Tesseract engine to recognize more than 100 languages.
|
||||||
- Documents are saved as PDF/A format which is designed for long term storage, alongside the unaltered originals.
|
- Documents are saved as PDF/A format which is designed for long term storage, alongside the unaltered originals.
|
||||||
- Uses machine-learning to automatically add tags, correspondents and document types to your documents.
|
- Uses machine-learning to automatically add tags, correspondents and document types to your documents.
|
||||||
- Supports PDF documents, images, plain text files, Office documents (Word, Excel, Powerpoint, and LibreOffice equivalents)[^1] and more.
|
- Supports PDF documents, images, plain text files, Office documents (Word, Excel, PowerPoint, and LibreOffice equivalents)[^1] and more.
|
||||||
- Paperless stores your documents plain on disk. Filenames and folders are managed by paperless and their format can be configured freely with different configurations assigned to different documents.
|
- Paperless stores your documents plain on disk. Filenames and folders are managed by paperless and their format can be configured freely with different configurations assigned to different documents.
|
||||||
- **Beautiful, modern web application** that features:
|
- **Beautiful, modern web application** that features:
|
||||||
- Customizable dashboard with statistics.
|
- Customizable dashboard with statistics.
|
||||||
|
@@ -445,7 +445,7 @@ are released, dependency support is confirmed, etc.
|
|||||||
13. Configure ImageMagick to allow processing of PDF documents. Most
|
13. Configure ImageMagick to allow processing of PDF documents. Most
|
||||||
distributions have this disabled by default, since PDF documents can
|
distributions have this disabled by default, since PDF documents can
|
||||||
contain malware. If you don't do this, paperless will fall back to
|
contain malware. If you don't do this, paperless will fall back to
|
||||||
ghostscript for certain steps such as thumbnail generation.
|
Ghostscript for certain steps such as thumbnail generation.
|
||||||
|
|
||||||
Edit `/etc/ImageMagick-6/policy.xml` and adjust
|
Edit `/etc/ImageMagick-6/policy.xml` and adjust
|
||||||
|
|
||||||
|
@@ -335,7 +335,7 @@ You may see errors when deleting documents like:
|
|||||||
Data too long for column 'transaction_id' at row 1
|
Data too long for column 'transaction_id' at row 1
|
||||||
```
|
```
|
||||||
|
|
||||||
This error can occur in installations which have upgraded from a version of Paperless-ngx that used Django 4 (Paperless-ngx versions prior to v2.13.0) with a MariaDB/MySQL database. Due to the backawards-incompatible change in Django 5, the column "documents_document.transaction_id" will need to be re-created, which can be done with a one-time run of the following management command:
|
This error can occur in installations which have upgraded from a version of Paperless-ngx that used Django 4 (Paperless-ngx versions prior to v2.13.0) with a MariaDB/MySQL database. Due to the backwards-incompatible change in Django 5, the column "documents_document.transaction_id" will need to be re-created, which can be done with a one-time run of the following management command:
|
||||||
|
|
||||||
```shell-session
|
```shell-session
|
||||||
$ python3 manage.py convert_mariadb_uuid
|
$ python3 manage.py convert_mariadb_uuid
|
||||||
|
@@ -30,6 +30,9 @@ Each document has data fields that you can assign to them:
|
|||||||
- A _document type_ is used to demarcate the type of a document such
|
- A _document type_ is used to demarcate the type of a document such
|
||||||
as letter, bank statement, invoice, contract, etc. It is used to
|
as letter, bank statement, invoice, contract, etc. It is used to
|
||||||
identify what a document is about.
|
identify what a document is about.
|
||||||
|
- The document _storage path_ is the location where the document files
|
||||||
|
are stored. See [Storage Paths](advanced_usage.md#storage-paths) for
|
||||||
|
more information.
|
||||||
- The _date added_ of a document is the date the document was scanned
|
- The _date added_ of a document is the date the document was scanned
|
||||||
into paperless. You cannot and should not change this date.
|
into paperless. You cannot and should not change this date.
|
||||||
- The _date created_ of a document is the date the document was
|
- The _date created_ of a document is the date the document was
|
||||||
|
@@ -52,12 +52,12 @@ if ! command -v wget &> /dev/null ; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if ! command -v docker &> /dev/null ; then
|
if ! command -v docker &> /dev/null ; then
|
||||||
echo "docker executable not found. Is docker installed?"
|
echo "docker executable not found. Is Docker installed?"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! docker compose &> /dev/null ; then
|
if ! docker compose &> /dev/null ; then
|
||||||
echo "docker compose plugin not found. Is docker compose installed?"
|
echo "docker compose plugin not found. Is Docker Compose installed?"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -66,7 +66,7 @@ fi
|
|||||||
if ! docker stats --no-stream &> /dev/null ; then
|
if ! docker stats --no-stream &> /dev/null ; then
|
||||||
echo ""
|
echo ""
|
||||||
echo "WARN: It look like the current user does not have Docker permissions."
|
echo "WARN: It look like the current user does not have Docker permissions."
|
||||||
echo "WARN: Use 'sudo usermod -aG docker $USER' to assign Docker permissions to the user (may require restarting shell)."
|
echo "WARN: Use 'sudo usermod -aG docker $USER' to assign Docker permissions to the user (may require restarting the shell)."
|
||||||
echo ""
|
echo ""
|
||||||
sleep 3
|
sleep 3
|
||||||
fi
|
fi
|
||||||
@@ -135,7 +135,7 @@ DATABASE_BACKEND=$ask_result
|
|||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "Paperless is able to use Apache Tika to support Office documents such as"
|
echo "Paperless is able to use Apache Tika to support Office documents such as"
|
||||||
echo "Word, Excel, Powerpoint, and Libreoffice equivalents. This feature"
|
echo "Word, Excel, PowerPoint, and LibreOffice equivalents. This feature"
|
||||||
echo "requires more resources due to the required services."
|
echo "requires more resources due to the required services."
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
@@ -157,7 +157,7 @@ echo ""
|
|||||||
echo "Specify the user id and group id you wish to run paperless as."
|
echo "Specify the user id and group id you wish to run paperless as."
|
||||||
echo "Paperless will also change ownership on the data, media and consume"
|
echo "Paperless will also change ownership on the data, media and consume"
|
||||||
echo "folder to the specified values, so it's a good idea to supply the user id"
|
echo "folder to the specified values, so it's a good idea to supply the user id"
|
||||||
echo "and group id of your unix user account."
|
echo "and group id of your Unix user account."
|
||||||
echo "If unsure, leave default."
|
echo "If unsure, leave default."
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
@@ -212,7 +212,7 @@ if [[ "$DATABASE_BACKEND" == "sqlite" ]] ; then
|
|||||||
echo -n "SQLite database, the "
|
echo -n "SQLite database, the "
|
||||||
fi
|
fi
|
||||||
echo "search index and other data."
|
echo "search index and other data."
|
||||||
echo "As with the media folder, leave empty to have this managed by docker."
|
echo "As with the media folder, leave empty to have this managed by Docker."
|
||||||
echo ""
|
echo ""
|
||||||
echo "CAUTION: If specified, you must specify an absolute path starting with /"
|
echo "CAUTION: If specified, you must specify an absolute path starting with /"
|
||||||
echo "or a relative path starting with ./ here."
|
echo "or a relative path starting with ./ here."
|
||||||
@@ -224,7 +224,7 @@ DATA_FOLDER=$ask_result
|
|||||||
if [[ "$DATABASE_BACKEND" == "postgres" || "$DATABASE_BACKEND" == "mariadb" ]] ; then
|
if [[ "$DATABASE_BACKEND" == "postgres" || "$DATABASE_BACKEND" == "mariadb" ]] ; then
|
||||||
echo ""
|
echo ""
|
||||||
echo "The database folder, where your database stores its data."
|
echo "The database folder, where your database stores its data."
|
||||||
echo "Leave empty to have this managed by docker."
|
echo "Leave empty to have this managed by Docker."
|
||||||
echo ""
|
echo ""
|
||||||
echo "CAUTION: If specified, you must specify an absolute path starting with /"
|
echo "CAUTION: If specified, you must specify an absolute path starting with /"
|
||||||
echo "or a relative path starting with ./ here."
|
echo "or a relative path starting with ./ here."
|
||||||
@@ -276,18 +276,18 @@ echo ""
|
|||||||
echo "Target folder: $TARGET_FOLDER"
|
echo "Target folder: $TARGET_FOLDER"
|
||||||
echo "Consume folder: $CONSUME_FOLDER"
|
echo "Consume folder: $CONSUME_FOLDER"
|
||||||
if [[ -z $MEDIA_FOLDER ]] ; then
|
if [[ -z $MEDIA_FOLDER ]] ; then
|
||||||
echo "Media folder: Managed by docker"
|
echo "Media folder: Managed by Docker"
|
||||||
else
|
else
|
||||||
echo "Media folder: $MEDIA_FOLDER"
|
echo "Media folder: $MEDIA_FOLDER"
|
||||||
fi
|
fi
|
||||||
if [[ -z $DATA_FOLDER ]] ; then
|
if [[ -z $DATA_FOLDER ]] ; then
|
||||||
echo "Data folder: Managed by docker"
|
echo "Data folder: Managed by Docker"
|
||||||
else
|
else
|
||||||
echo "Data folder: $DATA_FOLDER"
|
echo "Data folder: $DATA_FOLDER"
|
||||||
fi
|
fi
|
||||||
if [[ "$DATABASE_BACKEND" == "postgres" || "$DATABASE_BACKEND" == "mariadb" ]] ; then
|
if [[ "$DATABASE_BACKEND" == "postgres" || "$DATABASE_BACKEND" == "mariadb" ]] ; then
|
||||||
if [[ -z $DATABASE_FOLDER ]] ; then
|
if [[ -z $DATABASE_FOLDER ]] ; then
|
||||||
echo "Database folder: Managed by docker"
|
echo "Database folder: Managed by Docker"
|
||||||
else
|
else
|
||||||
echo "Database folder: $DATABASE_FOLDER"
|
echo "Database folder: $DATABASE_FOLDER"
|
||||||
fi
|
fi
|
||||||
|
@@ -26,6 +26,7 @@ dependencies = [
|
|||||||
"django~=5.1.7",
|
"django~=5.1.7",
|
||||||
"django-allauth[socialaccount,mfa]~=65.4.0",
|
"django-allauth[socialaccount,mfa]~=65.4.0",
|
||||||
"django-auditlog~=3.1.2",
|
"django-auditlog~=3.1.2",
|
||||||
|
"django-cachalot~=2.8.0",
|
||||||
"django-celery-results~=2.6.0",
|
"django-celery-results~=2.6.0",
|
||||||
"django-compression-middleware~=0.5.0",
|
"django-compression-middleware~=0.5.0",
|
||||||
"django-cors-headers~=4.7.0",
|
"django-cors-headers~=4.7.0",
|
||||||
@@ -43,14 +44,15 @@ dependencies = [
|
|||||||
"flower~=2.0.1",
|
"flower~=2.0.1",
|
||||||
"gotenberg-client~=0.10.0",
|
"gotenberg-client~=0.10.0",
|
||||||
"httpx-oauth~=0.16",
|
"httpx-oauth~=0.16",
|
||||||
"imap-tools~=1.10.0",
|
"imap-tools~=1.11.0",
|
||||||
"inotifyrecursive~=0.3",
|
"inotifyrecursive~=0.3",
|
||||||
"jinja2~=3.1.5",
|
"jinja2~=3.1.5",
|
||||||
"langdetect~=1.0.9",
|
"langdetect~=1.0.9",
|
||||||
"nltk~=3.9.1",
|
"nltk~=3.9.1",
|
||||||
"ocrmypdf~=16.10.0",
|
"ocrmypdf~=16.10.0",
|
||||||
"pathvalidate~=3.2.3",
|
"pathvalidate~=3.3.1",
|
||||||
"pdf2image~=1.17.0",
|
"pdf2image~=1.17.0",
|
||||||
|
"psycopg-pool",
|
||||||
"python-dateutil~=2.9.0",
|
"python-dateutil~=2.9.0",
|
||||||
"python-dotenv~=1.1.0",
|
"python-dotenv~=1.1.0",
|
||||||
"python-gnupg~=0.5.4",
|
"python-gnupg~=0.5.4",
|
||||||
@@ -59,7 +61,7 @@ dependencies = [
|
|||||||
"pyzbar~=0.1.9",
|
"pyzbar~=0.1.9",
|
||||||
"rapidfuzz~=3.13.0",
|
"rapidfuzz~=3.13.0",
|
||||||
"redis[hiredis]~=5.2.1",
|
"redis[hiredis]~=5.2.1",
|
||||||
"scikit-learn~=1.6.1",
|
"scikit-learn~=1.7.0",
|
||||||
"setproctitle~=1.3.4",
|
"setproctitle~=1.3.4",
|
||||||
"tika-client~=0.9.0",
|
"tika-client~=0.9.0",
|
||||||
"tqdm~=4.67.1",
|
"tqdm~=4.67.1",
|
||||||
@@ -73,12 +75,13 @@ optional-dependencies.mariadb = [
|
|||||||
"mysqlclient~=2.2.7",
|
"mysqlclient~=2.2.7",
|
||||||
]
|
]
|
||||||
optional-dependencies.postgres = [
|
optional-dependencies.postgres = [
|
||||||
"psycopg[c]==3.2.5",
|
"psycopg[c,pool]==3.2.9",
|
||||||
# Direct dependency for proper resolution of the pre-built wheels
|
# Direct dependency for proper resolution of the pre-built wheels
|
||||||
"psycopg-c==3.2.5",
|
"psycopg-c==3.2.9",
|
||||||
|
"psycopg-pool==3.2.6",
|
||||||
]
|
]
|
||||||
optional-dependencies.webserver = [
|
optional-dependencies.webserver = [
|
||||||
"granian[uvloop]~=2.3.2",
|
"granian[uvloop]~=2.4.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[dependency-groups]
|
[dependency-groups]
|
||||||
@@ -98,8 +101,8 @@ testing = [
|
|||||||
"daphne",
|
"daphne",
|
||||||
"factory-boy~=3.3.1",
|
"factory-boy~=3.3.1",
|
||||||
"imagehash",
|
"imagehash",
|
||||||
"pytest~=8.3.3",
|
"pytest~=8.4.1",
|
||||||
"pytest-cov~=6.0.0",
|
"pytest-cov~=6.2.1",
|
||||||
"pytest-django~=4.10.0",
|
"pytest-django~=4.10.0",
|
||||||
"pytest-env",
|
"pytest-env",
|
||||||
"pytest-httpx",
|
"pytest-httpx",
|
||||||
@@ -110,9 +113,9 @@ testing = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
lint = [
|
lint = [
|
||||||
"pre-commit~=4.1.0",
|
"pre-commit~=4.2.0",
|
||||||
"pre-commit-uv~=4.1.3",
|
"pre-commit-uv~=4.1.3",
|
||||||
"ruff~=0.9.9",
|
"ruff~=0.12.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
typing = [
|
typing = [
|
||||||
@@ -172,6 +175,7 @@ lint.extend-select = [
|
|||||||
]
|
]
|
||||||
lint.ignore = [
|
lint.ignore = [
|
||||||
"DJ001",
|
"DJ001",
|
||||||
|
"PLC0415",
|
||||||
"RUF012",
|
"RUF012",
|
||||||
"SIM105",
|
"SIM105",
|
||||||
]
|
]
|
||||||
@@ -237,6 +241,7 @@ testpaths = [
|
|||||||
"src/paperless_mail/tests/",
|
"src/paperless_mail/tests/",
|
||||||
"src/paperless_tesseract/tests/",
|
"src/paperless_tesseract/tests/",
|
||||||
"src/paperless_tika/tests",
|
"src/paperless_tika/tests",
|
||||||
|
"src/paperless_text/tests/",
|
||||||
]
|
]
|
||||||
addopts = [
|
addopts = [
|
||||||
"--pythonwarnings=all",
|
"--pythonwarnings=all",
|
||||||
@@ -300,8 +305,8 @@ environments = [
|
|||||||
[tool.uv.sources]
|
[tool.uv.sources]
|
||||||
# Markers are chosen to select these almost exclusively when building the Docker image
|
# Markers are chosen to select these almost exclusively when building the Docker image
|
||||||
psycopg-c = [
|
psycopg-c = [
|
||||||
{ url = "https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.5/psycopg_c-3.2.5-cp312-cp312-linux_x86_64.whl", marker = "sys_platform == 'linux' and platform_machine == 'x86_64' and python_version == '3.12'" },
|
{ url = "https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.9/psycopg_c-3.2.9-cp312-cp312-linux_x86_64.whl", marker = "sys_platform == 'linux' and platform_machine == 'x86_64' and python_version == '3.12'" },
|
||||||
{ url = "https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.5/psycopg_c-3.2.5-cp312-cp312-linux_aarch64.whl", marker = "sys_platform == 'linux' and platform_machine == 'aarch64' and python_version == '3.12'" },
|
{ url = "https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.9/psycopg_c-3.2.9-cp312-cp312-linux_aarch64.whl", marker = "sys_platform == 'linux' and platform_machine == 'aarch64' and python_version == '3.12'" },
|
||||||
]
|
]
|
||||||
zxing-cpp = [
|
zxing-cpp = [
|
||||||
{ url = "https://github.com/paperless-ngx/builder/releases/download/zxing-2.3.0/zxing_cpp-2.3.0-cp312-cp312-linux_x86_64.whl", marker = "sys_platform == 'linux' and platform_machine == 'x86_64' and python_version == '3.12'" },
|
{ url = "https://github.com/paperless-ngx/builder/releases/download/zxing-2.3.0/zxing_cpp-2.3.0-cp312-cp312-linux_x86_64.whl", marker = "sys_platform == 'linux' and platform_machine == 'x86_64' and python_version == '3.12'" },
|
||||||
|
@@ -48,6 +48,7 @@
|
|||||||
"sv-SE": "src/locale/messages.sv_SE.xlf",
|
"sv-SE": "src/locale/messages.sv_SE.xlf",
|
||||||
"tr-TR": "src/locale/messages.tr_TR.xlf",
|
"tr-TR": "src/locale/messages.tr_TR.xlf",
|
||||||
"uk-UA": "src/locale/messages.uk_UA.xlf",
|
"uk-UA": "src/locale/messages.uk_UA.xlf",
|
||||||
|
"vi-VN": "src/locale/messages.vi_VN.xlf",
|
||||||
"zh-CN": "src/locale/messages.zh_CN.xlf",
|
"zh-CN": "src/locale/messages.zh_CN.xlf",
|
||||||
"zh-TW": "src/locale/messages.zh_TW.xlf"
|
"zh-TW": "src/locale/messages.zh_TW.xlf"
|
||||||
}
|
}
|
||||||
@@ -60,10 +61,12 @@
|
|||||||
"path": "./extra-webpack.config.ts"
|
"path": "./extra-webpack.config.ts"
|
||||||
},
|
},
|
||||||
"outputPath": "dist/paperless-ui",
|
"outputPath": "dist/paperless-ui",
|
||||||
|
"main": "src/main.ts",
|
||||||
"outputHashing": "none",
|
"outputHashing": "none",
|
||||||
"index": "src/index.html",
|
"index": "src/index.html",
|
||||||
"main": "src/main.ts",
|
"polyfills": [
|
||||||
"polyfills": "src/polyfills.ts",
|
"src/polyfills.ts"
|
||||||
|
],
|
||||||
"tsConfig": "tsconfig.app.json",
|
"tsConfig": "tsconfig.app.json",
|
||||||
"localize": true,
|
"localize": true,
|
||||||
"assets": [
|
"assets": [
|
||||||
@@ -86,12 +89,15 @@
|
|||||||
"file-saver",
|
"file-saver",
|
||||||
"utif"
|
"utif"
|
||||||
],
|
],
|
||||||
"vendorChunk": true,
|
|
||||||
"extractLicenses": false,
|
"extractLicenses": false,
|
||||||
"buildOptimizer": false,
|
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"optimization": false,
|
"optimization": false,
|
||||||
"namedChunks": true
|
"namedChunks": true,
|
||||||
|
"stylePreprocessorOptions": {
|
||||||
|
"includePaths": [
|
||||||
|
"."
|
||||||
|
]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"configurations": {
|
"configurations": {
|
||||||
"production": {
|
"production": {
|
||||||
@@ -107,8 +113,6 @@
|
|||||||
"sourceMap": false,
|
"sourceMap": false,
|
||||||
"namedChunks": false,
|
"namedChunks": false,
|
||||||
"extractLicenses": true,
|
"extractLicenses": true,
|
||||||
"vendorChunk": false,
|
|
||||||
"buildOptimizer": true,
|
|
||||||
"budgets": [
|
"budgets": [
|
||||||
{
|
{
|
||||||
"type": "initial",
|
"type": "initial",
|
||||||
@@ -188,6 +192,30 @@
|
|||||||
},
|
},
|
||||||
"@angular-eslint/schematics:library": {
|
"@angular-eslint/schematics:library": {
|
||||||
"setParserOptionsProject": true
|
"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": "."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
1098
src-ui/messages.xlf
1098
src-ui/messages.xlf
File diff suppressed because it is too large
Load Diff
@@ -11,28 +11,28 @@
|
|||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/cdk": "^19.2.14",
|
"@angular/cdk": "^20.1.4",
|
||||||
"@angular/common": "~19.2.14",
|
"@angular/common": "~20.1.4",
|
||||||
"@angular/compiler": "~19.2.14",
|
"@angular/compiler": "~20.1.4",
|
||||||
"@angular/core": "~19.2.14",
|
"@angular/core": "~20.1.4",
|
||||||
"@angular/forms": "~19.2.14",
|
"@angular/forms": "~20.1.4",
|
||||||
"@angular/localize": "~19.2.14",
|
"@angular/localize": "~20.1.4",
|
||||||
"@angular/platform-browser": "~19.2.14",
|
"@angular/platform-browser": "~20.1.4",
|
||||||
"@angular/platform-browser-dynamic": "~19.2.14",
|
"@angular/platform-browser-dynamic": "~20.1.4",
|
||||||
"@angular/router": "~19.2.14",
|
"@angular/router": "~20.1.4",
|
||||||
"@ng-bootstrap/ng-bootstrap": "^18.0.0",
|
"@ng-bootstrap/ng-bootstrap": "^19.0.1",
|
||||||
"@ng-select/ng-select": "^14.9.0",
|
"@ng-select/ng-select": "^20.0.1",
|
||||||
"@ngneat/dirty-check-forms": "^3.0.3",
|
"@ngneat/dirty-check-forms": "^3.0.3",
|
||||||
"@popperjs/core": "^2.11.8",
|
"@popperjs/core": "^2.11.8",
|
||||||
"bootstrap": "^5.3.6",
|
"bootstrap": "^5.3.7",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"mime-names": "^1.0.0",
|
"mime-names": "^1.0.0",
|
||||||
"ng2-pdf-viewer": "^10.4.0",
|
"ng2-pdf-viewer": "^10.4.0",
|
||||||
"ngx-bootstrap-icons": "^1.9.3",
|
"ngx-bootstrap-icons": "^1.9.3",
|
||||||
"ngx-color": "^10.0.0",
|
"ngx-color": "^10.0.0",
|
||||||
"ngx-cookie-service": "^19.1.2",
|
"ngx-cookie-service": "^20.0.1",
|
||||||
"ngx-device-detector": "^9.0.0",
|
"ngx-device-detector": "^10.0.2",
|
||||||
"ngx-ui-tour-ng-bootstrap": "^16.0.0",
|
"ngx-ui-tour-ng-bootstrap": "^17.0.1",
|
||||||
"rxjs": "^7.8.2",
|
"rxjs": "^7.8.2",
|
||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"utif": "^3.1.0",
|
"utif": "^3.1.0",
|
||||||
@@ -40,35 +40,35 @@
|
|||||||
"zone.js": "^0.15.1"
|
"zone.js": "^0.15.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular-builders/custom-webpack": "^19.0.1",
|
"@angular-builders/custom-webpack": "^20.0.0",
|
||||||
"@angular-builders/jest": "^19.0.1",
|
"@angular-builders/jest": "^20.0.0",
|
||||||
"@angular-devkit/build-angular": "^19.2.14",
|
"@angular-devkit/core": "^20.1.4",
|
||||||
"@angular-devkit/core": "^19.2.14",
|
"@angular-devkit/schematics": "^20.1.4",
|
||||||
"@angular-devkit/schematics": "^19.2.14",
|
"@angular-eslint/builder": "20.1.1",
|
||||||
"@angular-eslint/builder": "19.7.0",
|
"@angular-eslint/eslint-plugin": "20.1.1",
|
||||||
"@angular-eslint/eslint-plugin": "19.7.0",
|
"@angular-eslint/eslint-plugin-template": "20.1.1",
|
||||||
"@angular-eslint/eslint-plugin-template": "19.7.0",
|
"@angular-eslint/schematics": "20.1.1",
|
||||||
"@angular-eslint/schematics": "19.7.0",
|
"@angular-eslint/template-parser": "20.1.1",
|
||||||
"@angular-eslint/template-parser": "19.7.0",
|
"@angular/build": "^20.1.4",
|
||||||
"@angular/cli": "~19.2.14",
|
"@angular/cli": "~20.1.4",
|
||||||
"@angular/compiler-cli": "~19.2.14",
|
"@angular/compiler-cli": "~20.1.4",
|
||||||
"@codecov/webpack-plugin": "^1.9.1",
|
"@codecov/webpack-plugin": "^1.9.1",
|
||||||
"@playwright/test": "^1.51.1",
|
"@playwright/test": "^1.54.2",
|
||||||
"@types/jest": "^29.5.14",
|
"@types/jest": "^30.0.0",
|
||||||
"@types/node": "^22.15.29",
|
"@types/node": "^24.1.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.33.0",
|
"@typescript-eslint/eslint-plugin": "^8.38.0",
|
||||||
"@typescript-eslint/parser": "^8.33.0",
|
"@typescript-eslint/parser": "^8.38.0",
|
||||||
"@typescript-eslint/utils": "^8.33.0",
|
"@typescript-eslint/utils": "^8.38.0",
|
||||||
"eslint": "^9.28.0",
|
"eslint": "^9.32.0",
|
||||||
"jest": "29.7.0",
|
"jest": "30.0.5",
|
||||||
"jest-environment-jsdom": "^29.7.0",
|
"jest-environment-jsdom": "^30.0.5",
|
||||||
"jest-junit": "^16.0.0",
|
"jest-junit": "^16.0.0",
|
||||||
"jest-preset-angular": "^14.5.5",
|
"jest-preset-angular": "^15.0.0",
|
||||||
"jest-websocket-mock": "^2.5.0",
|
"jest-websocket-mock": "^2.5.0",
|
||||||
"prettier-plugin-organize-imports": "^4.1.0",
|
"prettier-plugin-organize-imports": "^4.2.0",
|
||||||
"ts-node": "~10.9.1",
|
"ts-node": "~10.9.1",
|
||||||
"typescript": "^5.5.4",
|
"typescript": "^5.8.3",
|
||||||
"webpack": "^5.98.0"
|
"webpack": "^5.101.0"
|
||||||
},
|
},
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
"onlyBuiltDependencies": [
|
"onlyBuiltDependencies": [
|
||||||
@@ -78,6 +78,5 @@
|
|||||||
"lmdb",
|
"lmdb",
|
||||||
"msgpackr-extract"
|
"msgpackr-extract"
|
||||||
]
|
]
|
||||||
},
|
}
|
||||||
"typings": "./src/typings.d.ts"
|
|
||||||
}
|
}
|
||||||
|
7493
src-ui/pnpm-lock.yaml
generated
7493
src-ui/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,12 +1,16 @@
|
|||||||
import '@angular/localize/init'
|
import '@angular/localize/init'
|
||||||
import { jest } from '@jest/globals'
|
import { jest } from '@jest/globals'
|
||||||
import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone'
|
import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone'
|
||||||
import { TextDecoder, TextEncoder } from 'util'
|
import { TextDecoder, TextEncoder } from 'node:util'
|
||||||
if (process.env.NODE_ENV === 'test') {
|
if (process.env.NODE_ENV === 'test') {
|
||||||
setupZoneTestEnv()
|
setupZoneTestEnv()
|
||||||
}
|
}
|
||||||
global.TextEncoder = TextEncoder
|
;(globalThis as any).TextEncoder = TextEncoder as unknown as {
|
||||||
global.TextDecoder = TextDecoder
|
new (): TextEncoder
|
||||||
|
}
|
||||||
|
;(globalThis as any).TextDecoder = TextDecoder as unknown as {
|
||||||
|
new (): TextDecoder
|
||||||
|
}
|
||||||
|
|
||||||
import { registerLocaleData } from '@angular/common'
|
import { registerLocaleData } from '@angular/common'
|
||||||
import localeAf from '@angular/common/locales/af'
|
import localeAf from '@angular/common/locales/af'
|
||||||
@@ -40,6 +44,7 @@ import localeSr from '@angular/common/locales/sr'
|
|||||||
import localeSv from '@angular/common/locales/sv'
|
import localeSv from '@angular/common/locales/sv'
|
||||||
import localeTr from '@angular/common/locales/tr'
|
import localeTr from '@angular/common/locales/tr'
|
||||||
import localeUk from '@angular/common/locales/uk'
|
import localeUk from '@angular/common/locales/uk'
|
||||||
|
import localeVi from '@angular/common/locales/vi'
|
||||||
import localeZh from '@angular/common/locales/zh'
|
import localeZh from '@angular/common/locales/zh'
|
||||||
import localeZhHant from '@angular/common/locales/zh-Hant'
|
import localeZhHant from '@angular/common/locales/zh-Hant'
|
||||||
|
|
||||||
@@ -75,6 +80,7 @@ registerLocaleData(localeSr)
|
|||||||
registerLocaleData(localeSv)
|
registerLocaleData(localeSv)
|
||||||
registerLocaleData(localeTr)
|
registerLocaleData(localeTr)
|
||||||
registerLocaleData(localeUk)
|
registerLocaleData(localeUk)
|
||||||
|
registerLocaleData(localeVi)
|
||||||
registerLocaleData(localeZh)
|
registerLocaleData(localeZh)
|
||||||
registerLocaleData(localeZhHant)
|
registerLocaleData(localeZhHant)
|
||||||
|
|
||||||
@@ -114,10 +120,6 @@ if (!URL.revokeObjectURL) {
|
|||||||
Object.defineProperty(window.URL, 'revokeObjectURL', { value: jest.fn() })
|
Object.defineProperty(window.URL, 'revokeObjectURL', { value: jest.fn() })
|
||||||
}
|
}
|
||||||
Object.defineProperty(window, 'ResizeObserver', { value: mock() })
|
Object.defineProperty(window, 'ResizeObserver', { value: mock() })
|
||||||
Object.defineProperty(window, 'location', {
|
|
||||||
configurable: true,
|
|
||||||
value: { reload: jest.fn() },
|
|
||||||
})
|
|
||||||
|
|
||||||
HTMLCanvasElement.prototype.getContext = <
|
HTMLCanvasElement.prototype.getContext = <
|
||||||
typeof HTMLCanvasElement.prototype.getContext
|
typeof HTMLCanvasElement.prototype.getContext
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { Component, OnDestroy, OnInit, Renderer2 } from '@angular/core'
|
import { Component, inject, OnDestroy, OnInit, Renderer2 } from '@angular/core'
|
||||||
import { Router, RouterOutlet } from '@angular/router'
|
import { Router, RouterOutlet } from '@angular/router'
|
||||||
import { TourNgBootstrapModule, TourService } from 'ngx-ui-tour-ng-bootstrap'
|
import { TourNgBootstrapModule, TourService } from 'ngx-ui-tour-ng-bootstrap'
|
||||||
import { first, Subscription } from 'rxjs'
|
import { first, Subscription } from 'rxjs'
|
||||||
@@ -29,22 +29,22 @@ import { WebsocketStatusService } from './services/websocket-status.service'
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class AppComponent implements OnInit, OnDestroy {
|
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
|
newDocumentSubscription: Subscription
|
||||||
successSubscription: Subscription
|
successSubscription: Subscription
|
||||||
failedSubscription: 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
|
let anyWindow = window as any
|
||||||
anyWindow.pdfWorkerSrc = 'assets/js/pdf.worker.min.mjs'
|
anyWindow.pdfWorkerSrc = 'assets/js/pdf.worker.min.mjs'
|
||||||
this.settings.updateAppearanceSettings()
|
this.settings.updateAppearanceSettings()
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { AsyncPipe } from '@angular/common'
|
import { AsyncPipe } from '@angular/common'
|
||||||
import { Component, OnDestroy, OnInit } from '@angular/core'
|
import { Component, OnDestroy, OnInit, inject } from '@angular/core'
|
||||||
import {
|
import {
|
||||||
AbstractControl,
|
AbstractControl,
|
||||||
FormControl,
|
FormControl,
|
||||||
@@ -57,6 +57,10 @@ export class ConfigComponent
|
|||||||
extends LoadingComponentWithPermissions
|
extends LoadingComponentWithPermissions
|
||||||
implements OnInit, OnDestroy, DirtyComponent
|
implements OnInit, OnDestroy, DirtyComponent
|
||||||
{
|
{
|
||||||
|
private configService = inject(ConfigService)
|
||||||
|
private toastService = inject(ToastService)
|
||||||
|
private settingsService = inject(SettingsService)
|
||||||
|
|
||||||
public readonly ConfigOptionType = ConfigOptionType
|
public readonly ConfigOptionType = ConfigOptionType
|
||||||
|
|
||||||
// generated dynamically
|
// generated dynamically
|
||||||
@@ -77,11 +81,7 @@ export class ConfigComponent
|
|||||||
storeSub: Subscription
|
storeSub: Subscription
|
||||||
isDirty$: Observable<boolean>
|
isDirty$: Observable<boolean>
|
||||||
|
|
||||||
constructor(
|
constructor() {
|
||||||
private configService: ConfigService,
|
|
||||||
private toastService: ToastService,
|
|
||||||
private settingsService: SettingsService
|
|
||||||
) {
|
|
||||||
super()
|
super()
|
||||||
this.configForm.addControl('id', new FormControl())
|
this.configForm.addControl('id', new FormControl())
|
||||||
PaperlessConfigOptions.forEach((option) => {
|
PaperlessConfigOptions.forEach((option) => {
|
||||||
|
@@ -5,6 +5,7 @@ import {
|
|||||||
OnDestroy,
|
OnDestroy,
|
||||||
OnInit,
|
OnInit,
|
||||||
ViewChild,
|
ViewChild,
|
||||||
|
inject,
|
||||||
} from '@angular/core'
|
} from '@angular/core'
|
||||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||||
import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap'
|
||||||
@@ -28,12 +29,8 @@ export class LogsComponent
|
|||||||
extends LoadingComponentWithPermissions
|
extends LoadingComponentWithPermissions
|
||||||
implements OnInit, OnDestroy
|
implements OnInit, OnDestroy
|
||||||
{
|
{
|
||||||
constructor(
|
private logService = inject(LogService)
|
||||||
private logService: LogService,
|
private changedetectorRef = inject(ChangeDetectorRef)
|
||||||
private changedetectorRef: ChangeDetectorRef
|
|
||||||
) {
|
|
||||||
super()
|
|
||||||
}
|
|
||||||
|
|
||||||
public logs: string[] = []
|
public logs: string[] = []
|
||||||
|
|
||||||
|
@@ -176,6 +176,7 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<pngx-input-check i18n-title title="Show warning when closing saved views with unsaved changes" formControlName="savedViewsWarnOnUnsavedChange"></pngx-input-check>
|
<pngx-input-check i18n-title title="Show warning when closing saved views with unsaved changes" formControlName="savedViewsWarnOnUnsavedChange"></pngx-input-check>
|
||||||
|
<pngx-input-check i18n-title title="Show document counts in sidebar saved views" formControlName="sidebarViewsShowCount"></pngx-input-check>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@@ -31,10 +31,12 @@ import { CustomDatePipe } from 'src/app/pipes/custom-date.pipe'
|
|||||||
import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe'
|
import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe'
|
||||||
import { PermissionsService } from 'src/app/services/permissions.service'
|
import { PermissionsService } from 'src/app/services/permissions.service'
|
||||||
import { GroupService } from 'src/app/services/rest/group.service'
|
import { GroupService } from 'src/app/services/rest/group.service'
|
||||||
|
import { SavedViewService } from 'src/app/services/rest/saved-view.service'
|
||||||
import { UserService } from 'src/app/services/rest/user.service'
|
import { UserService } from 'src/app/services/rest/user.service'
|
||||||
import { SettingsService } from 'src/app/services/settings.service'
|
import { SettingsService } from 'src/app/services/settings.service'
|
||||||
import { SystemStatusService } from 'src/app/services/system-status.service'
|
import { SystemStatusService } from 'src/app/services/system-status.service'
|
||||||
import { Toast, ToastService } from 'src/app/services/toast.service'
|
import { Toast, ToastService } from 'src/app/services/toast.service'
|
||||||
|
import * as navUtils from 'src/app/utils/navigation'
|
||||||
import { ConfirmButtonComponent } from '../../common/confirm-button/confirm-button.component'
|
import { ConfirmButtonComponent } from '../../common/confirm-button/confirm-button.component'
|
||||||
import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component'
|
import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component'
|
||||||
import { CheckComponent } from '../../common/input/check/check.component'
|
import { CheckComponent } from '../../common/input/check/check.component'
|
||||||
@@ -72,6 +74,7 @@ describe('SettingsComponent', () => {
|
|||||||
let groupService: GroupService
|
let groupService: GroupService
|
||||||
let modalService: NgbModal
|
let modalService: NgbModal
|
||||||
let systemStatusService: SystemStatusService
|
let systemStatusService: SystemStatusService
|
||||||
|
let savedViewsService: SavedViewService
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
@@ -122,6 +125,7 @@ describe('SettingsComponent', () => {
|
|||||||
permissionsService = TestBed.inject(PermissionsService)
|
permissionsService = TestBed.inject(PermissionsService)
|
||||||
modalService = TestBed.inject(NgbModal)
|
modalService = TestBed.inject(NgbModal)
|
||||||
systemStatusService = TestBed.inject(SystemStatusService)
|
systemStatusService = TestBed.inject(SystemStatusService)
|
||||||
|
savedViewsService = TestBed.inject(SavedViewService)
|
||||||
jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true)
|
jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true)
|
||||||
jest
|
jest
|
||||||
.spyOn(permissionsService, 'currentUserHasObjectPermissions')
|
.spyOn(permissionsService, 'currentUserHasObjectPermissions')
|
||||||
@@ -212,7 +216,7 @@ describe('SettingsComponent', () => {
|
|||||||
expect(toastErrorSpy).toHaveBeenCalled()
|
expect(toastErrorSpy).toHaveBeenCalled()
|
||||||
expect(storeSpy).toHaveBeenCalled()
|
expect(storeSpy).toHaveBeenCalled()
|
||||||
expect(appearanceSettingsSpy).not.toHaveBeenCalled()
|
expect(appearanceSettingsSpy).not.toHaveBeenCalled()
|
||||||
expect(setSpy).toHaveBeenCalledTimes(29)
|
expect(setSpy).toHaveBeenCalledTimes(30)
|
||||||
|
|
||||||
// succeed
|
// succeed
|
||||||
storeSpy.mockReturnValueOnce(of(true))
|
storeSpy.mockReturnValueOnce(of(true))
|
||||||
@@ -222,6 +226,9 @@ describe('SettingsComponent', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should offer reload if settings changes require', () => {
|
it('should offer reload if settings changes require', () => {
|
||||||
|
const reloadSpy = jest
|
||||||
|
.spyOn(navUtils, 'locationReload')
|
||||||
|
.mockImplementation(() => {})
|
||||||
completeSetup()
|
completeSetup()
|
||||||
let toast: Toast
|
let toast: Toast
|
||||||
toastService.getToasts().subscribe((t) => (toast = t[0]))
|
toastService.getToasts().subscribe((t) => (toast = t[0]))
|
||||||
@@ -238,6 +245,7 @@ describe('SettingsComponent', () => {
|
|||||||
|
|
||||||
expect(toast.actionName).toEqual('Reload now')
|
expect(toast.actionName).toEqual('Reload now')
|
||||||
toast.action()
|
toast.action()
|
||||||
|
expect(reloadSpy).toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should allow setting theme color, visually apply change immediately but not save', () => {
|
it('should allow setting theme color, visually apply change immediately but not save', () => {
|
||||||
@@ -266,7 +274,7 @@ describe('SettingsComponent', () => {
|
|||||||
)
|
)
|
||||||
completeSetup(userService)
|
completeSetup(userService)
|
||||||
fixture.detectChanges()
|
fixture.detectChanges()
|
||||||
expect(toastErrorSpy).toBeCalled()
|
expect(toastErrorSpy).toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should show errors on load if load groups failure', () => {
|
it('should show errors on load if load groups failure', () => {
|
||||||
@@ -278,7 +286,7 @@ describe('SettingsComponent', () => {
|
|||||||
)
|
)
|
||||||
completeSetup(groupService)
|
completeSetup(groupService)
|
||||||
fixture.detectChanges()
|
fixture.detectChanges()
|
||||||
expect(toastErrorSpy).toBeCalled()
|
expect(toastErrorSpy).toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should load system status on initialize, show errors if needed', () => {
|
it('should load system status on initialize, show errors if needed', () => {
|
||||||
@@ -345,4 +353,14 @@ describe('SettingsComponent', () => {
|
|||||||
component.reset()
|
component.reset()
|
||||||
expect(component.settingsForm.get('themeColor').value).toEqual('')
|
expect(component.settingsForm.get('themeColor').value).toEqual('')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should trigger maybeRefreshDocumentCounts on settings save', () => {
|
||||||
|
completeSetup()
|
||||||
|
const maybeRefreshSpy = jest.spyOn(
|
||||||
|
savedViewsService,
|
||||||
|
'maybeRefreshDocumentCounts'
|
||||||
|
)
|
||||||
|
settingsService.settingsSaved.emit(true)
|
||||||
|
expect(maybeRefreshSpy).toHaveBeenCalled()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@@ -2,10 +2,10 @@ import { AsyncPipe, ViewportScroller } from '@angular/common'
|
|||||||
import {
|
import {
|
||||||
AfterViewInit,
|
AfterViewInit,
|
||||||
Component,
|
Component,
|
||||||
Inject,
|
|
||||||
LOCALE_ID,
|
LOCALE_ID,
|
||||||
OnDestroy,
|
OnDestroy,
|
||||||
OnInit,
|
OnInit,
|
||||||
|
inject,
|
||||||
} from '@angular/core'
|
} from '@angular/core'
|
||||||
import {
|
import {
|
||||||
FormControl,
|
FormControl,
|
||||||
@@ -49,6 +49,7 @@ import {
|
|||||||
PermissionsService,
|
PermissionsService,
|
||||||
} from 'src/app/services/permissions.service'
|
} from 'src/app/services/permissions.service'
|
||||||
import { GroupService } from 'src/app/services/rest/group.service'
|
import { GroupService } from 'src/app/services/rest/group.service'
|
||||||
|
import { SavedViewService } from 'src/app/services/rest/saved-view.service'
|
||||||
import { UserService } from 'src/app/services/rest/user.service'
|
import { UserService } from 'src/app/services/rest/user.service'
|
||||||
import {
|
import {
|
||||||
LanguageOption,
|
LanguageOption,
|
||||||
@@ -56,6 +57,7 @@ import {
|
|||||||
} from 'src/app/services/settings.service'
|
} from 'src/app/services/settings.service'
|
||||||
import { SystemStatusService } from 'src/app/services/system-status.service'
|
import { SystemStatusService } from 'src/app/services/system-status.service'
|
||||||
import { Toast, ToastService } from 'src/app/services/toast.service'
|
import { Toast, ToastService } from 'src/app/services/toast.service'
|
||||||
|
import { locationReload } from 'src/app/utils/navigation'
|
||||||
import { CheckComponent } from '../../common/input/check/check.component'
|
import { CheckComponent } from '../../common/input/check/check.component'
|
||||||
import { ColorComponent } from '../../common/input/color/color.component'
|
import { ColorComponent } from '../../common/input/color/color.component'
|
||||||
import { PermissionsGroupComponent } from '../../common/input/permissions/permissions-group/permissions-group.component'
|
import { PermissionsGroupComponent } from '../../common/input/permissions/permissions-group/permissions-group.component'
|
||||||
@@ -104,6 +106,21 @@ export class SettingsComponent
|
|||||||
extends ComponentWithPermissions
|
extends ComponentWithPermissions
|
||||||
implements OnInit, AfterViewInit, OnDestroy, DirtyComponent
|
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)
|
||||||
|
private savedViewsService = inject(SavedViewService)
|
||||||
|
|
||||||
activeNavID: number
|
activeNavID: number
|
||||||
|
|
||||||
settingsForm = new FormGroup({
|
settingsForm = new FormGroup({
|
||||||
@@ -138,6 +155,7 @@ export class SettingsComponent
|
|||||||
notificationsConsumerSuppressOnDashboard: new FormControl(null),
|
notificationsConsumerSuppressOnDashboard: new FormControl(null),
|
||||||
|
|
||||||
savedViewsWarnOnUnsavedChange: new FormControl(null),
|
savedViewsWarnOnUnsavedChange: new FormControl(null),
|
||||||
|
sidebarViewsShowCount: new FormControl(null),
|
||||||
})
|
})
|
||||||
|
|
||||||
SettingsNavIDs = SettingsNavIDs
|
SettingsNavIDs = SettingsNavIDs
|
||||||
@@ -179,24 +197,11 @@ 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()
|
super()
|
||||||
this.settings.settingsSaved.subscribe(() => {
|
this.settings.settingsSaved.subscribe(() => {
|
||||||
if (!this.savePending) this.initialize()
|
if (!this.savePending) this.initialize()
|
||||||
|
this.savedViewsService.maybeRefreshDocumentCounts()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -308,6 +313,9 @@ export class SettingsComponent
|
|||||||
savedViewsWarnOnUnsavedChange: this.settings.get(
|
savedViewsWarnOnUnsavedChange: this.settings.get(
|
||||||
SETTINGS_KEYS.SAVED_VIEWS_WARN_ON_UNSAVED_CHANGE
|
SETTINGS_KEYS.SAVED_VIEWS_WARN_ON_UNSAVED_CHANGE
|
||||||
),
|
),
|
||||||
|
sidebarViewsShowCount: this.settings.get(
|
||||||
|
SETTINGS_KEYS.SIDEBAR_VIEWS_SHOW_COUNT
|
||||||
|
),
|
||||||
defaultPermsOwner: this.settings.get(SETTINGS_KEYS.DEFAULT_PERMS_OWNER),
|
defaultPermsOwner: this.settings.get(SETTINGS_KEYS.DEFAULT_PERMS_OWNER),
|
||||||
defaultPermsViewUsers: this.settings.get(
|
defaultPermsViewUsers: this.settings.get(
|
||||||
SETTINGS_KEYS.DEFAULT_PERMS_VIEW_USERS
|
SETTINGS_KEYS.DEFAULT_PERMS_VIEW_USERS
|
||||||
@@ -485,6 +493,10 @@ export class SettingsComponent
|
|||||||
SETTINGS_KEYS.SAVED_VIEWS_WARN_ON_UNSAVED_CHANGE,
|
SETTINGS_KEYS.SAVED_VIEWS_WARN_ON_UNSAVED_CHANGE,
|
||||||
this.settingsForm.value.savedViewsWarnOnUnsavedChange
|
this.settingsForm.value.savedViewsWarnOnUnsavedChange
|
||||||
)
|
)
|
||||||
|
this.settings.set(
|
||||||
|
SETTINGS_KEYS.SIDEBAR_VIEWS_SHOW_COUNT,
|
||||||
|
this.settingsForm.value.sidebarViewsShowCount
|
||||||
|
)
|
||||||
this.settings.set(
|
this.settings.set(
|
||||||
SETTINGS_KEYS.DEFAULT_PERMS_OWNER,
|
SETTINGS_KEYS.DEFAULT_PERMS_OWNER,
|
||||||
this.settingsForm.value.defaultPermsOwner
|
this.settingsForm.value.defaultPermsOwner
|
||||||
@@ -539,7 +551,7 @@ export class SettingsComponent
|
|||||||
savedToast.content = $localize`Settings were saved successfully. Reload is required to apply some changes.`
|
savedToast.content = $localize`Settings were saved successfully. Reload is required to apply some changes.`
|
||||||
savedToast.actionName = $localize`Reload now`
|
savedToast.actionName = $localize`Reload now`
|
||||||
savedToast.action = () => {
|
savedToast.action = () => {
|
||||||
location.reload()
|
locationReload()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { NgTemplateOutlet, SlicePipe } from '@angular/common'
|
import { NgTemplateOutlet, SlicePipe } from '@angular/common'
|
||||||
import { Component, OnDestroy, OnInit } from '@angular/core'
|
import { Component, inject, OnDestroy, OnInit } from '@angular/core'
|
||||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||||
import { Router } from '@angular/router'
|
import { Router } from '@angular/router'
|
||||||
import {
|
import {
|
||||||
@@ -69,6 +69,10 @@ export class TasksComponent
|
|||||||
extends LoadingComponentWithPermissions
|
extends LoadingComponentWithPermissions
|
||||||
implements OnInit, OnDestroy
|
implements OnInit, OnDestroy
|
||||||
{
|
{
|
||||||
|
tasksService = inject(TasksService)
|
||||||
|
private modalService = inject(NgbModal)
|
||||||
|
private readonly router = inject(Router)
|
||||||
|
|
||||||
public activeTab: TaskTab
|
public activeTab: TaskTab
|
||||||
public selectedTasks: Set<number> = new Set()
|
public selectedTasks: Set<number> = new Set()
|
||||||
public togggleAll: boolean = false
|
public togggleAll: boolean = false
|
||||||
@@ -105,14 +109,6 @@ export class TasksComponent
|
|||||||
: $localize`Dismiss all`
|
: $localize`Dismiss all`
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
|
||||||
public tasksService: TasksService,
|
|
||||||
private modalService: NgbModal,
|
|
||||||
private readonly router: Router
|
|
||||||
) {
|
|
||||||
super()
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.tasksService.reload()
|
this.tasksService.reload()
|
||||||
timer(5000, 5000)
|
timer(5000, 5000)
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { Component, OnDestroy } from '@angular/core'
|
import { Component, OnDestroy, inject } from '@angular/core'
|
||||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||||
import { Router } from '@angular/router'
|
import { Router } from '@angular/router'
|
||||||
import {
|
import {
|
||||||
@@ -36,19 +36,19 @@ export class TrashComponent
|
|||||||
extends LoadingComponentWithPermissions
|
extends LoadingComponentWithPermissions
|
||||||
implements OnDestroy
|
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 documentsInTrash: Document[] = []
|
||||||
public selectedDocuments: Set<number> = new Set()
|
public selectedDocuments: Set<number> = new Set()
|
||||||
public allToggled: boolean = false
|
public allToggled: boolean = false
|
||||||
public page: number = 1
|
public page: number = 1
|
||||||
public totalDocuments: number
|
public totalDocuments: number
|
||||||
|
|
||||||
constructor(
|
constructor() {
|
||||||
private trashService: TrashService,
|
|
||||||
private toastService: ToastService,
|
|
||||||
private modalService: NgbModal,
|
|
||||||
private settingsService: SettingsService,
|
|
||||||
private router: Router
|
|
||||||
) {
|
|
||||||
super()
|
super()
|
||||||
this.reload()
|
this.reload()
|
||||||
}
|
}
|
||||||
|
@@ -19,6 +19,7 @@ import { GroupService } from 'src/app/services/rest/group.service'
|
|||||||
import { UserService } from 'src/app/services/rest/user.service'
|
import { UserService } from 'src/app/services/rest/user.service'
|
||||||
import { SettingsService } from 'src/app/services/settings.service'
|
import { SettingsService } from 'src/app/services/settings.service'
|
||||||
import { ToastService } from 'src/app/services/toast.service'
|
import { ToastService } from 'src/app/services/toast.service'
|
||||||
|
import * as navUtils from 'src/app/utils/navigation'
|
||||||
import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component'
|
import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component'
|
||||||
import { GroupEditDialogComponent } from '../../common/edit-dialog/group-edit-dialog/group-edit-dialog.component'
|
import { GroupEditDialogComponent } from '../../common/edit-dialog/group-edit-dialog/group-edit-dialog.component'
|
||||||
import { UserEditDialogComponent } from '../../common/edit-dialog/user-edit-dialog/user-edit-dialog.component'
|
import { UserEditDialogComponent } from '../../common/edit-dialog/user-edit-dialog/user-edit-dialog.component'
|
||||||
@@ -107,7 +108,7 @@ describe('UsersAndGroupsComponent', () => {
|
|||||||
const toastErrorSpy = jest.spyOn(toastService, 'showError')
|
const toastErrorSpy = jest.spyOn(toastService, 'showError')
|
||||||
const toastInfoSpy = jest.spyOn(toastService, 'showInfo')
|
const toastInfoSpy = jest.spyOn(toastService, 'showInfo')
|
||||||
editDialog.failed.emit()
|
editDialog.failed.emit()
|
||||||
expect(toastErrorSpy).toBeCalled()
|
expect(toastErrorSpy).toHaveBeenCalled()
|
||||||
settingsService.currentUser = users[1] // simulate logged in as different user
|
settingsService.currentUser = users[1] // simulate logged in as different user
|
||||||
editDialog.succeeded.emit(users[0])
|
editDialog.succeeded.emit(users[0])
|
||||||
expect(toastInfoSpy).toHaveBeenCalledWith(
|
expect(toastInfoSpy).toHaveBeenCalledWith(
|
||||||
@@ -130,7 +131,7 @@ describe('UsersAndGroupsComponent', () => {
|
|||||||
throwError(() => new Error('error deleting user'))
|
throwError(() => new Error('error deleting user'))
|
||||||
)
|
)
|
||||||
deleteDialog.confirm()
|
deleteDialog.confirm()
|
||||||
expect(toastErrorSpy).toBeCalled()
|
expect(toastErrorSpy).toHaveBeenCalled()
|
||||||
deleteSpy.mockReturnValueOnce(of(true))
|
deleteSpy.mockReturnValueOnce(of(true))
|
||||||
deleteDialog.confirm()
|
deleteDialog.confirm()
|
||||||
expect(listAllSpy).toHaveBeenCalled()
|
expect(listAllSpy).toHaveBeenCalled()
|
||||||
@@ -142,19 +143,18 @@ describe('UsersAndGroupsComponent', () => {
|
|||||||
let modal: NgbModalRef
|
let modal: NgbModalRef
|
||||||
modalService.activeInstances.subscribe((refs) => (modal = refs[0]))
|
modalService.activeInstances.subscribe((refs) => (modal = refs[0]))
|
||||||
component.editUser(users[0])
|
component.editUser(users[0])
|
||||||
|
const navSpy = jest
|
||||||
|
.spyOn(navUtils, 'setLocationHref')
|
||||||
|
.mockImplementation(() => {})
|
||||||
const editDialog = modal.componentInstance as UserEditDialogComponent
|
const editDialog = modal.componentInstance as UserEditDialogComponent
|
||||||
editDialog.passwordIsSet = true
|
editDialog.passwordIsSet = true
|
||||||
settingsService.currentUser = users[0] // simulate logged in as same user
|
settingsService.currentUser = users[0] // simulate logged in as same user
|
||||||
editDialog.succeeded.emit(users[0])
|
editDialog.succeeded.emit(users[0])
|
||||||
fixture.detectChanges()
|
fixture.detectChanges()
|
||||||
Object.defineProperty(window, 'location', {
|
|
||||||
value: {
|
|
||||||
href: 'http://localhost/',
|
|
||||||
},
|
|
||||||
writable: true, // possibility to override
|
|
||||||
})
|
|
||||||
tick(2600)
|
tick(2600)
|
||||||
expect(window.location.href).toContain('logout')
|
expect(navSpy).toHaveBeenCalledWith(
|
||||||
|
`${window.location.origin}/accounts/logout/?next=/accounts/login/?next=/`
|
||||||
|
)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
it('should support edit / create group, show error if needed', () => {
|
it('should support edit / create group, show error if needed', () => {
|
||||||
@@ -166,7 +166,7 @@ describe('UsersAndGroupsComponent', () => {
|
|||||||
const toastErrorSpy = jest.spyOn(toastService, 'showError')
|
const toastErrorSpy = jest.spyOn(toastService, 'showError')
|
||||||
const toastInfoSpy = jest.spyOn(toastService, 'showInfo')
|
const toastInfoSpy = jest.spyOn(toastService, 'showInfo')
|
||||||
editDialog.failed.emit()
|
editDialog.failed.emit()
|
||||||
expect(toastErrorSpy).toBeCalled()
|
expect(toastErrorSpy).toHaveBeenCalled()
|
||||||
editDialog.succeeded.emit(groups[0])
|
editDialog.succeeded.emit(groups[0])
|
||||||
expect(toastInfoSpy).toHaveBeenCalledWith(
|
expect(toastInfoSpy).toHaveBeenCalledWith(
|
||||||
`Saved group "${groups[0].name}".`
|
`Saved group "${groups[0].name}".`
|
||||||
@@ -188,7 +188,7 @@ describe('UsersAndGroupsComponent', () => {
|
|||||||
throwError(() => new Error('error deleting group'))
|
throwError(() => new Error('error deleting group'))
|
||||||
)
|
)
|
||||||
deleteDialog.confirm()
|
deleteDialog.confirm()
|
||||||
expect(toastErrorSpy).toBeCalled()
|
expect(toastErrorSpy).toHaveBeenCalled()
|
||||||
deleteSpy.mockReturnValueOnce(of(true))
|
deleteSpy.mockReturnValueOnce(of(true))
|
||||||
deleteDialog.confirm()
|
deleteDialog.confirm()
|
||||||
expect(listAllSpy).toHaveBeenCalled()
|
expect(listAllSpy).toHaveBeenCalled()
|
||||||
@@ -210,7 +210,7 @@ describe('UsersAndGroupsComponent', () => {
|
|||||||
)
|
)
|
||||||
completeSetup(userService)
|
completeSetup(userService)
|
||||||
fixture.detectChanges()
|
fixture.detectChanges()
|
||||||
expect(toastErrorSpy).toBeCalled()
|
expect(toastErrorSpy).toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should show errors on load if load groups failure', () => {
|
it('should show errors on load if load groups failure', () => {
|
||||||
@@ -222,6 +222,6 @@ describe('UsersAndGroupsComponent', () => {
|
|||||||
)
|
)
|
||||||
completeSetup(groupService)
|
completeSetup(groupService)
|
||||||
fixture.detectChanges()
|
fixture.detectChanges()
|
||||||
expect(toastErrorSpy).toBeCalled()
|
expect(toastErrorSpy).toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { Component, OnDestroy, OnInit } from '@angular/core'
|
import { Component, OnDestroy, OnInit, inject } from '@angular/core'
|
||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
||||||
import { Subject, first, takeUntil } from 'rxjs'
|
import { Subject, first, takeUntil } from 'rxjs'
|
||||||
@@ -10,6 +10,7 @@ import { GroupService } from 'src/app/services/rest/group.service'
|
|||||||
import { UserService } from 'src/app/services/rest/user.service'
|
import { UserService } from 'src/app/services/rest/user.service'
|
||||||
import { SettingsService } from 'src/app/services/settings.service'
|
import { SettingsService } from 'src/app/services/settings.service'
|
||||||
import { ToastService } from 'src/app/services/toast.service'
|
import { ToastService } from 'src/app/services/toast.service'
|
||||||
|
import { setLocationHref } from 'src/app/utils/navigation'
|
||||||
import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component'
|
import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component'
|
||||||
import { EditDialogMode } from '../../common/edit-dialog/edit-dialog.component'
|
import { EditDialogMode } from '../../common/edit-dialog/edit-dialog.component'
|
||||||
import { GroupEditDialogComponent } from '../../common/edit-dialog/group-edit-dialog/group-edit-dialog.component'
|
import { GroupEditDialogComponent } from '../../common/edit-dialog/group-edit-dialog/group-edit-dialog.component'
|
||||||
@@ -31,22 +32,18 @@ export class UsersAndGroupsComponent
|
|||||||
extends ComponentWithPermissions
|
extends ComponentWithPermissions
|
||||||
implements OnInit, OnDestroy
|
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[]
|
users: User[]
|
||||||
groups: Group[]
|
groups: Group[]
|
||||||
|
|
||||||
unsubscribeNotifier: Subject<any> = new Subject()
|
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 {
|
ngOnInit(): void {
|
||||||
this.usersService
|
this.usersService
|
||||||
.listAll(null, null, { full_perms: true })
|
.listAll(null, null, { full_perms: true })
|
||||||
@@ -97,7 +94,9 @@ export class UsersAndGroupsComponent
|
|||||||
$localize`Password has been changed, you will be logged out momentarily.`
|
$localize`Password has been changed, you will be logged out momentarily.`
|
||||||
)
|
)
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
window.location.href = `${window.location.origin}/accounts/logout/?next=/accounts/login/?next=/`
|
setLocationHref(
|
||||||
|
`${window.location.origin}/accounts/logout/?next=/accounts/login/?next=/`
|
||||||
|
)
|
||||||
}, 2500)
|
}, 2500)
|
||||||
} else {
|
} else {
|
||||||
this.toastService.showInfo(
|
this.toastService.showInfo(
|
||||||
|
@@ -112,7 +112,14 @@
|
|||||||
routerLinkActive="active" (click)="closeMenu()" [ngbPopover]="view.name"
|
routerLinkActive="active" (click)="closeMenu()" [ngbPopover]="view.name"
|
||||||
[disablePopover]="!slimSidebarEnabled" placement="end" container="body" triggers="mouseenter:mouseleave"
|
[disablePopover]="!slimSidebarEnabled" placement="end" container="body" triggers="mouseenter:mouseleave"
|
||||||
popoverClass="popover-slim">
|
popoverClass="popover-slim">
|
||||||
<i-bs class="me-1" name="funnel"></i-bs><span> {{view.name}}</span>
|
<i-bs class="me-1" name="funnel"></i-bs><span> {{view.name}}
|
||||||
|
@if (showSidebarCounts && !slimSidebarEnabled) {
|
||||||
|
<span><span class="badge bg-info text-dark ms-2 d-inline">{{ savedViewService.getDocumentCount(view) }}</span></span>
|
||||||
|
}
|
||||||
|
</span>
|
||||||
|
@if (showSidebarCounts && slimSidebarEnabled) {
|
||||||
|
<span class="badge bg-info text-dark position-absolute top-0 end-0 d-none d-md-block">{{ savedViewService.getDocumentCount(view) }}</span>
|
||||||
|
}
|
||||||
</a>
|
</a>
|
||||||
@if (settingsService.organizingSidebarSavedViews) {
|
@if (settingsService.organizingSidebarSavedViews) {
|
||||||
<div class="position-absolute end-0 top-0 px-3 py-2" [class.me-n3]="slimSidebarEnabled" cdkDragHandle>
|
<div class="position-absolute end-0 top-0 px-3 py-2" [class.me-n3]="slimSidebarEnabled" cdkDragHandle>
|
||||||
|
@@ -92,6 +92,7 @@ describe('AppFrameComponent', () => {
|
|||||||
let router: Router
|
let router: Router
|
||||||
let savedViewSpy
|
let savedViewSpy
|
||||||
let modalService: NgbModal
|
let modalService: NgbModal
|
||||||
|
let maybeRefreshSpy
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
@@ -113,7 +114,11 @@ describe('AppFrameComponent', () => {
|
|||||||
{
|
{
|
||||||
provide: SavedViewService,
|
provide: SavedViewService,
|
||||||
useValue: {
|
useValue: {
|
||||||
reload: () => {},
|
reload: (fn: any) => {
|
||||||
|
if (fn) {
|
||||||
|
fn()
|
||||||
|
}
|
||||||
|
},
|
||||||
listAll: () =>
|
listAll: () =>
|
||||||
of({
|
of({
|
||||||
all: [saved_views.map((v) => v.id)],
|
all: [saved_views.map((v) => v.id)],
|
||||||
@@ -121,6 +126,8 @@ describe('AppFrameComponent', () => {
|
|||||||
results: saved_views,
|
results: saved_views,
|
||||||
}),
|
}),
|
||||||
sidebarViews: saved_views.filter((v) => v.show_in_sidebar),
|
sidebarViews: saved_views.filter((v) => v.show_in_sidebar),
|
||||||
|
getDocumentCount: (view: SavedView) => 5,
|
||||||
|
maybeRefreshDocumentCounts: () => {},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
PermissionsService,
|
PermissionsService,
|
||||||
@@ -169,6 +176,7 @@ describe('AppFrameComponent', () => {
|
|||||||
jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true)
|
jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true)
|
||||||
|
|
||||||
savedViewSpy = jest.spyOn(savedViewService, 'reload')
|
savedViewSpy = jest.spyOn(savedViewService, 'reload')
|
||||||
|
maybeRefreshSpy = jest.spyOn(savedViewService, 'maybeRefreshDocumentCounts')
|
||||||
|
|
||||||
fixture = TestBed.createComponent(AppFrameComponent)
|
fixture = TestBed.createComponent(AppFrameComponent)
|
||||||
component = fixture.componentInstance
|
component = fixture.componentInstance
|
||||||
@@ -359,4 +367,8 @@ describe('AppFrameComponent', () => {
|
|||||||
expect(toastErrorSpy).toHaveBeenCalledTimes(2)
|
expect(toastErrorSpy).toHaveBeenCalledTimes(2)
|
||||||
expect(toastInfoSpy).toHaveBeenCalledTimes(3)
|
expect(toastInfoSpy).toHaveBeenCalledTimes(3)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should call maybeRefreshDocumentCounts after saved views reload', () => {
|
||||||
|
expect(maybeRefreshSpy).toHaveBeenCalled()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@@ -6,7 +6,7 @@ import {
|
|||||||
moveItemInArray,
|
moveItemInArray,
|
||||||
} from '@angular/cdk/drag-drop'
|
} from '@angular/cdk/drag-drop'
|
||||||
import { NgClass } from '@angular/common'
|
import { NgClass } from '@angular/common'
|
||||||
import { Component, HostListener, OnInit } from '@angular/core'
|
import { Component, HostListener, inject, OnInit } from '@angular/core'
|
||||||
import { ActivatedRoute, Router, RouterModule } from '@angular/router'
|
import { ActivatedRoute, Router, RouterModule } from '@angular/router'
|
||||||
import {
|
import {
|
||||||
NgbCollapseModule,
|
NgbCollapseModule,
|
||||||
@@ -74,26 +74,27 @@ export class AppFrameComponent
|
|||||||
extends ComponentWithPermissions
|
extends ComponentWithPermissions
|
||||||
implements OnInit, ComponentCanDeactivate
|
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
|
appRemoteVersion: AppRemoteVersion
|
||||||
|
|
||||||
isMenuCollapsed: boolean = true
|
isMenuCollapsed: boolean = true
|
||||||
|
|
||||||
slimSidebarAnimating: boolean = false
|
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()
|
super()
|
||||||
|
const permissionsService = this.permissionsService
|
||||||
|
|
||||||
if (
|
if (
|
||||||
permissionsService.currentUserCan(
|
permissionsService.currentUserCan(
|
||||||
@@ -101,7 +102,9 @@ export class AppFrameComponent
|
|||||||
PermissionType.SavedView
|
PermissionType.SavedView
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
this.savedViewService.reload()
|
this.savedViewService.reload(() => {
|
||||||
|
this.savedViewService.maybeRefreshDocumentCounts()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,4 +285,8 @@ export class AppFrameComponent
|
|||||||
onLogout() {
|
onLogout() {
|
||||||
this.openDocumentsService.closeAll()
|
this.openDocumentsService.closeAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get showSidebarCounts(): boolean {
|
||||||
|
return this.settingsService.get(SETTINGS_KEYS.SIDEBAR_VIEWS_SHOW_COUNT)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -6,6 +6,7 @@ import {
|
|||||||
QueryList,
|
QueryList,
|
||||||
ViewChild,
|
ViewChild,
|
||||||
ViewChildren,
|
ViewChildren,
|
||||||
|
inject,
|
||||||
} from '@angular/core'
|
} from '@angular/core'
|
||||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||||
import { Router } from '@angular/router'
|
import { Router } from '@angular/router'
|
||||||
@@ -69,6 +70,17 @@ import { WorkflowEditDialogComponent } from '../../common/edit-dialog/workflow-e
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class GlobalSearchComponent implements OnInit {
|
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 DataType = DataType
|
||||||
public query: string
|
public query: string
|
||||||
public queryDebounce: Subject<string>
|
public queryDebounce: Subject<string>
|
||||||
@@ -90,18 +102,7 @@ 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 = new Subject<string>()
|
||||||
|
|
||||||
this.queryDebounce
|
this.queryDebounce
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { Component, OnDestroy, OnInit } from '@angular/core'
|
import { Component, OnDestroy, OnInit, inject } from '@angular/core'
|
||||||
import {
|
import {
|
||||||
NgbDropdownModule,
|
NgbDropdownModule,
|
||||||
NgbProgressbarModule,
|
NgbProgressbarModule,
|
||||||
@@ -20,7 +20,7 @@ import { ToastComponent } from '../../common/toast/toast.component'
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class ToastsDropdownComponent implements OnInit, OnDestroy {
|
export class ToastsDropdownComponent implements OnInit, OnDestroy {
|
||||||
constructor(public toastService: ToastService) {}
|
toastService = inject(ToastService)
|
||||||
|
|
||||||
private subscription: Subscription
|
private subscription: Subscription
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { DecimalPipe } from '@angular/common'
|
import { DecimalPipe } from '@angular/common'
|
||||||
import { Component, EventEmitter, Input, Output } from '@angular/core'
|
import { Component, EventEmitter, Input, Output, inject } from '@angular/core'
|
||||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { Subject } from 'rxjs'
|
import { Subject } from 'rxjs'
|
||||||
import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe'
|
import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe'
|
||||||
@@ -12,9 +12,7 @@ import { LoadingComponentWithPermissions } from '../../loading-component/loading
|
|||||||
imports: [DecimalPipe, SafeHtmlPipe],
|
imports: [DecimalPipe, SafeHtmlPipe],
|
||||||
})
|
})
|
||||||
export class ConfirmDialogComponent extends LoadingComponentWithPermissions {
|
export class ConfirmDialogComponent extends LoadingComponentWithPermissions {
|
||||||
constructor(public activeModal: NgbActiveModal) {
|
activeModal = inject(NgbActiveModal)
|
||||||
super()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Output()
|
@Output()
|
||||||
public confirmClicked = new EventEmitter()
|
public confirmClicked = new EventEmitter()
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
import { Component, TemplateRef, ViewChild } from '@angular/core'
|
import { Component, TemplateRef, ViewChild, inject } from '@angular/core'
|
||||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
|
||||||
import {
|
import {
|
||||||
PDFDocumentProxy,
|
PDFDocumentProxy,
|
||||||
PdfViewerComponent,
|
PdfViewerComponent,
|
||||||
@@ -17,6 +16,8 @@ import { ConfirmDialogComponent } from '../confirm-dialog.component'
|
|||||||
imports: [PdfViewerModule, FormsModule, ReactiveFormsModule, SafeHtmlPipe],
|
imports: [PdfViewerModule, FormsModule, ReactiveFormsModule, SafeHtmlPipe],
|
||||||
})
|
})
|
||||||
export class DeletePagesConfirmDialogComponent extends ConfirmDialogComponent {
|
export class DeletePagesConfirmDialogComponent extends ConfirmDialogComponent {
|
||||||
|
private documentService = inject(DocumentService)
|
||||||
|
|
||||||
public documentID: number
|
public documentID: number
|
||||||
public pages: number[] = []
|
public pages: number[] = []
|
||||||
public currentPage: number = 1
|
public currentPage: number = 1
|
||||||
@@ -34,11 +35,8 @@ export class DeletePagesConfirmDialogComponent extends ConfirmDialogComponent {
|
|||||||
return this.documentService.getPreviewUrl(this.documentID)
|
return this.documentService.getPreviewUrl(this.documentID)
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor() {
|
||||||
activeModal: NgbActiveModal,
|
super()
|
||||||
private documentService: DocumentService
|
|
||||||
) {
|
|
||||||
super(activeModal)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public pdfPreviewLoaded(pdf: PDFDocumentProxy) {
|
public pdfPreviewLoaded(pdf: PDFDocumentProxy) {
|
||||||
|
@@ -3,9 +3,8 @@ import {
|
|||||||
DragDropModule,
|
DragDropModule,
|
||||||
moveItemInArray,
|
moveItemInArray,
|
||||||
} from '@angular/cdk/drag-drop'
|
} from '@angular/cdk/drag-drop'
|
||||||
import { Component, OnInit } from '@angular/core'
|
import { Component, OnInit, inject } from '@angular/core'
|
||||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
|
||||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
||||||
import { takeUntil } from 'rxjs'
|
import { takeUntil } from 'rxjs'
|
||||||
import { Document } from 'src/app/data/document'
|
import { Document } from 'src/app/data/document'
|
||||||
@@ -28,6 +27,9 @@ export class MergeConfirmDialogComponent
|
|||||||
extends ConfirmDialogComponent
|
extends ConfirmDialogComponent
|
||||||
implements OnInit
|
implements OnInit
|
||||||
{
|
{
|
||||||
|
private documentService = inject(DocumentService)
|
||||||
|
private permissionService = inject(PermissionsService)
|
||||||
|
|
||||||
public documentIDs: number[] = []
|
public documentIDs: number[] = []
|
||||||
public archiveFallback: boolean = false
|
public archiveFallback: boolean = false
|
||||||
public deleteOriginals: boolean = false
|
public deleteOriginals: boolean = false
|
||||||
@@ -38,12 +40,8 @@ export class MergeConfirmDialogComponent
|
|||||||
|
|
||||||
public metadataDocumentID: number = -1
|
public metadataDocumentID: number = -1
|
||||||
|
|
||||||
constructor(
|
constructor() {
|
||||||
activeModal: NgbActiveModal,
|
super()
|
||||||
private documentService: DocumentService,
|
|
||||||
private permissionService: PermissionsService
|
|
||||||
) {
|
|
||||||
super(activeModal)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
import { NgStyle } from '@angular/common'
|
import { NgStyle } from '@angular/common'
|
||||||
import { Component } from '@angular/core'
|
import { Component, inject } from '@angular/core'
|
||||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
|
||||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
||||||
import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe'
|
import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe'
|
||||||
import { DocumentService } from 'src/app/services/rest/document.service'
|
import { DocumentService } from 'src/app/services/rest/document.service'
|
||||||
@@ -13,6 +12,8 @@ import { ConfirmDialogComponent } from '../confirm-dialog.component'
|
|||||||
imports: [NgStyle, NgxBootstrapIconsModule, SafeHtmlPipe],
|
imports: [NgStyle, NgxBootstrapIconsModule, SafeHtmlPipe],
|
||||||
})
|
})
|
||||||
export class RotateConfirmDialogComponent extends ConfirmDialogComponent {
|
export class RotateConfirmDialogComponent extends ConfirmDialogComponent {
|
||||||
|
documentService = inject(DocumentService)
|
||||||
|
|
||||||
public documentID: number
|
public documentID: number
|
||||||
public showPDFNote: boolean = true
|
public showPDFNote: boolean = true
|
||||||
|
|
||||||
@@ -25,11 +26,8 @@ export class RotateConfirmDialogComponent extends ConfirmDialogComponent {
|
|||||||
return degrees
|
return degrees
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor() {
|
||||||
activeModal: NgbActiveModal,
|
super()
|
||||||
public documentService: DocumentService
|
|
||||||
) {
|
|
||||||
super(activeModal)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rotate(clockwise: boolean = true) {
|
rotate(clockwise: boolean = true) {
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
import { Component, OnInit } from '@angular/core'
|
import { Component, OnInit, inject } from '@angular/core'
|
||||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
|
||||||
import { PDFDocumentProxy, PdfViewerModule } from 'ng2-pdf-viewer'
|
import { PDFDocumentProxy, PdfViewerModule } from 'ng2-pdf-viewer'
|
||||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
||||||
import { Document } from 'src/app/data/document'
|
import { Document } from 'src/app/data/document'
|
||||||
@@ -23,6 +22,9 @@ export class SplitConfirmDialogComponent
|
|||||||
extends ConfirmDialogComponent
|
extends ConfirmDialogComponent
|
||||||
implements OnInit
|
implements OnInit
|
||||||
{
|
{
|
||||||
|
private documentService = inject(DocumentService)
|
||||||
|
private permissionService = inject(PermissionsService)
|
||||||
|
|
||||||
public get pagesString(): string {
|
public get pagesString(): string {
|
||||||
let pagesStr = ''
|
let pagesStr = ''
|
||||||
|
|
||||||
@@ -62,12 +64,8 @@ export class SplitConfirmDialogComponent
|
|||||||
return this.documentService.getPreviewUrl(this.documentID)
|
return this.documentService.getPreviewUrl(this.documentID)
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor() {
|
||||||
activeModal: NgbActiveModal,
|
super()
|
||||||
private documentService: DocumentService,
|
|
||||||
private permissionService: PermissionsService
|
|
||||||
) {
|
|
||||||
super(activeModal)
|
|
||||||
this.confirmButtonEnabled = this.pages.size > 0
|
this.confirmButtonEnabled = this.pages.size > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { CurrencyPipe, getLocaleCurrencyCode } from '@angular/common'
|
import { CurrencyPipe, getLocaleCurrencyCode } from '@angular/common'
|
||||||
import { Component, Inject, Input, LOCALE_ID, OnInit } from '@angular/core'
|
import { Component, Input, LOCALE_ID, OnInit, inject } from '@angular/core'
|
||||||
import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { takeUntil } from 'rxjs'
|
import { takeUntil } from 'rxjs'
|
||||||
import { CustomField, CustomFieldDataType } from 'src/app/data/custom-field'
|
import { CustomField, CustomFieldDataType } from 'src/app/data/custom-field'
|
||||||
@@ -20,6 +20,9 @@ export class CustomFieldDisplayComponent
|
|||||||
extends LoadingComponentWithPermissions
|
extends LoadingComponentWithPermissions
|
||||||
implements OnInit
|
implements OnInit
|
||||||
{
|
{
|
||||||
|
private customFieldService = inject(CustomFieldsService)
|
||||||
|
private documentService = inject(DocumentService)
|
||||||
|
|
||||||
CustomFieldDataType = CustomFieldDataType
|
CustomFieldDataType = CustomFieldDataType
|
||||||
|
|
||||||
private _document: Document
|
private _document: Document
|
||||||
@@ -63,11 +66,9 @@ export class CustomFieldDisplayComponent
|
|||||||
|
|
||||||
private defaultCurrencyCode: any
|
private defaultCurrencyCode: any
|
||||||
|
|
||||||
constructor(
|
constructor() {
|
||||||
private customFieldService: CustomFieldsService,
|
const currentLocale = inject(LOCALE_ID)
|
||||||
private documentService: DocumentService,
|
|
||||||
@Inject(LOCALE_ID) currentLocale: string
|
|
||||||
) {
|
|
||||||
super()
|
super()
|
||||||
this.defaultCurrencyCode = getLocaleCurrencyCode(currentLocale)
|
this.defaultCurrencyCode = getLocaleCurrencyCode(currentLocale)
|
||||||
this.customFieldService.listAll().subscribe((r) => {
|
this.customFieldService.listAll().subscribe((r) => {
|
||||||
|
@@ -7,6 +7,7 @@ import {
|
|||||||
QueryList,
|
QueryList,
|
||||||
ViewChild,
|
ViewChild,
|
||||||
ViewChildren,
|
ViewChildren,
|
||||||
|
inject,
|
||||||
} from '@angular/core'
|
} from '@angular/core'
|
||||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||||
import { NgbDropdownModule, NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbDropdownModule, NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
@@ -37,6 +38,11 @@ import { CustomFieldEditDialogComponent } from '../edit-dialog/custom-field-edit
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class CustomFieldsDropdownComponent extends LoadingComponentWithPermissions {
|
export class CustomFieldsDropdownComponent extends LoadingComponentWithPermissions {
|
||||||
|
private customFieldsService = inject(CustomFieldsService)
|
||||||
|
private modalService = inject(NgbModal)
|
||||||
|
private toastService = inject(ToastService)
|
||||||
|
private permissionsService = inject(PermissionsService)
|
||||||
|
|
||||||
public popperOptions = pngxPopperOptions
|
public popperOptions = pngxPopperOptions
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
@@ -78,12 +84,7 @@ export class CustomFieldsDropdownComponent extends LoadingComponentWithPermissio
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor() {
|
||||||
private customFieldsService: CustomFieldsService,
|
|
||||||
private modalService: NgbModal,
|
|
||||||
private toastService: ToastService,
|
|
||||||
private permissionsService: PermissionsService
|
|
||||||
) {
|
|
||||||
super()
|
super()
|
||||||
this.getFields()
|
this.getFields()
|
||||||
}
|
}
|
||||||
|
@@ -2,6 +2,7 @@ import { NgTemplateOutlet } from '@angular/common'
|
|||||||
import {
|
import {
|
||||||
Component,
|
Component,
|
||||||
EventEmitter,
|
EventEmitter,
|
||||||
|
inject,
|
||||||
Input,
|
Input,
|
||||||
Output,
|
Output,
|
||||||
QueryList,
|
QueryList,
|
||||||
@@ -178,6 +179,8 @@ export class CustomFieldQueriesModel {
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class CustomFieldsQueryDropdownComponent extends LoadingComponentWithPermissions {
|
export class CustomFieldsQueryDropdownComponent extends LoadingComponentWithPermissions {
|
||||||
|
protected customFieldsService = inject(CustomFieldsService)
|
||||||
|
|
||||||
public CustomFieldQueryComponentType = CustomFieldQueryElementType
|
public CustomFieldQueryComponentType = CustomFieldQueryElementType
|
||||||
public CustomFieldQueryOperator = CustomFieldQueryOperator
|
public CustomFieldQueryOperator = CustomFieldQueryOperator
|
||||||
public CustomFieldDataType = CustomFieldDataType
|
public CustomFieldDataType = CustomFieldDataType
|
||||||
@@ -243,9 +246,9 @@ export class CustomFieldsQueryDropdownComponent extends LoadingComponentWithPerm
|
|||||||
|
|
||||||
customFields: CustomField[] = []
|
customFields: CustomField[] = []
|
||||||
|
|
||||||
public readonly today: string = new Date().toISOString().split('T')[0]
|
public readonly today: string = new Date().toLocaleDateString('en-CA')
|
||||||
|
|
||||||
constructor(protected customFieldsService: CustomFieldsService) {
|
constructor() {
|
||||||
super()
|
super()
|
||||||
this.selectionModel = new CustomFieldQueriesModel()
|
this.selectionModel = new CustomFieldQueriesModel()
|
||||||
this.getFields()
|
this.getFields()
|
||||||
|
@@ -6,6 +6,7 @@ import {
|
|||||||
OnDestroy,
|
OnDestroy,
|
||||||
OnInit,
|
OnInit,
|
||||||
Output,
|
Output,
|
||||||
|
inject,
|
||||||
} from '@angular/core'
|
} from '@angular/core'
|
||||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||||
import {
|
import {
|
||||||
@@ -63,7 +64,9 @@ export enum RelativeDate {
|
|||||||
export class DatesDropdownComponent implements OnInit, OnDestroy {
|
export class DatesDropdownComponent implements OnInit, OnDestroy {
|
||||||
public popperOptions = pngxPopperOptions
|
public popperOptions = pngxPopperOptions
|
||||||
|
|
||||||
constructor(settings: SettingsService) {
|
constructor() {
|
||||||
|
const settings = inject(SettingsService)
|
||||||
|
|
||||||
this.datePlaceHolder = settings.getLocalizedDateInputFormat()
|
this.datePlaceHolder = settings.getLocalizedDateInputFormat()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,7 +165,7 @@ export class DatesDropdownComponent implements OnInit, OnDestroy {
|
|||||||
@Input()
|
@Input()
|
||||||
placement: string = 'bottom-start'
|
placement: string = 'bottom-start'
|
||||||
|
|
||||||
public readonly today: string = new Date().toISOString().split('T')[0]
|
public readonly today: string = new Date().toLocaleDateString('en-CA')
|
||||||
|
|
||||||
get isActive(): boolean {
|
get isActive(): boolean {
|
||||||
return (
|
return (
|
||||||
|
@@ -13,8 +13,6 @@
|
|||||||
<pngx-input-select i18n-title title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></pngx-input-select>
|
<pngx-input-select i18n-title title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></pngx-input-select>
|
||||||
@if (patternRequired) {
|
@if (patternRequired) {
|
||||||
<pngx-input-text i18n-title title="Matching pattern" formControlName="match" [error]="error?.match"></pngx-input-text>
|
<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>
|
<pngx-input-check i18n-title title="Case insensitive" formControlName="is_insensitive" novalidate></pngx-input-check>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,11 +1,10 @@
|
|||||||
import { Component } from '@angular/core'
|
import { Component, inject } from '@angular/core'
|
||||||
import {
|
import {
|
||||||
FormControl,
|
FormControl,
|
||||||
FormGroup,
|
FormGroup,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
} from '@angular/forms'
|
} from '@angular/forms'
|
||||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
|
||||||
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
|
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
|
||||||
import { Correspondent } from 'src/app/data/correspondent'
|
import { Correspondent } from 'src/app/data/correspondent'
|
||||||
import { DEFAULT_MATCHING_ALGORITHM } from 'src/app/data/matching-model'
|
import { DEFAULT_MATCHING_ALGORITHM } from 'src/app/data/matching-model'
|
||||||
@@ -13,6 +12,7 @@ import { IfOwnerDirective } from 'src/app/directives/if-owner.directive'
|
|||||||
import { CorrespondentService } from 'src/app/services/rest/correspondent.service'
|
import { CorrespondentService } from 'src/app/services/rest/correspondent.service'
|
||||||
import { UserService } from 'src/app/services/rest/user.service'
|
import { UserService } from 'src/app/services/rest/user.service'
|
||||||
import { SettingsService } from 'src/app/services/settings.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 { PermissionsFormComponent } from '../../input/permissions/permissions-form/permissions-form.component'
|
||||||
import { SelectComponent } from '../../input/select/select.component'
|
import { SelectComponent } from '../../input/select/select.component'
|
||||||
import { TextComponent } from '../../input/text/text.component'
|
import { TextComponent } from '../../input/text/text.component'
|
||||||
@@ -22,6 +22,7 @@ import { TextComponent } from '../../input/text/text.component'
|
|||||||
templateUrl: './correspondent-edit-dialog.component.html',
|
templateUrl: './correspondent-edit-dialog.component.html',
|
||||||
styleUrls: ['./correspondent-edit-dialog.component.scss'],
|
styleUrls: ['./correspondent-edit-dialog.component.scss'],
|
||||||
imports: [
|
imports: [
|
||||||
|
CheckComponent,
|
||||||
SelectComponent,
|
SelectComponent,
|
||||||
PermissionsFormComponent,
|
PermissionsFormComponent,
|
||||||
TextComponent,
|
TextComponent,
|
||||||
@@ -31,13 +32,11 @@ import { TextComponent } from '../../input/text/text.component'
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class CorrespondentEditDialogComponent extends EditDialogComponent<Correspondent> {
|
export class CorrespondentEditDialogComponent extends EditDialogComponent<Correspondent> {
|
||||||
constructor(
|
constructor() {
|
||||||
service: CorrespondentService,
|
super()
|
||||||
activeModal: NgbActiveModal,
|
this.service = inject(CorrespondentService)
|
||||||
userService: UserService,
|
this.userService = inject(UserService)
|
||||||
settingsService: SettingsService
|
this.settingsService = inject(SettingsService)
|
||||||
) {
|
|
||||||
super(service, activeModal, userService, settingsService)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getCreateTitle() {
|
getCreateTitle() {
|
||||||
|
@@ -5,6 +5,7 @@ import {
|
|||||||
OnInit,
|
OnInit,
|
||||||
QueryList,
|
QueryList,
|
||||||
ViewChildren,
|
ViewChildren,
|
||||||
|
inject,
|
||||||
} from '@angular/core'
|
} from '@angular/core'
|
||||||
import {
|
import {
|
||||||
FormArray,
|
FormArray,
|
||||||
@@ -13,7 +14,6 @@ import {
|
|||||||
FormsModule,
|
FormsModule,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
} from '@angular/forms'
|
} from '@angular/forms'
|
||||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
|
||||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
||||||
import { takeUntil } from 'rxjs'
|
import { takeUntil } from 'rxjs'
|
||||||
import {
|
import {
|
||||||
@@ -54,13 +54,11 @@ export class CustomFieldEditDialogComponent
|
|||||||
.select_options as FormArray
|
.select_options as FormArray
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor() {
|
||||||
service: CustomFieldsService,
|
super()
|
||||||
activeModal: NgbActiveModal,
|
this.service = inject(CustomFieldsService)
|
||||||
userService: UserService,
|
this.userService = inject(UserService)
|
||||||
settingsService: SettingsService
|
this.settingsService = inject(SettingsService)
|
||||||
) {
|
|
||||||
super(service, activeModal, userService, settingsService)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
@@ -14,8 +14,6 @@
|
|||||||
<pngx-input-select i18n-title title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></pngx-input-select>
|
<pngx-input-select i18n-title title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></pngx-input-select>
|
||||||
@if (patternRequired) {
|
@if (patternRequired) {
|
||||||
<pngx-input-text i18n-title title="Matching pattern" formControlName="match" [error]="error?.match"></pngx-input-text>
|
<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>
|
<pngx-input-check i18n-title title="Case insensitive" formControlName="is_insensitive"></pngx-input-check>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,11 +1,10 @@
|
|||||||
import { Component } from '@angular/core'
|
import { Component, inject } from '@angular/core'
|
||||||
import {
|
import {
|
||||||
FormControl,
|
FormControl,
|
||||||
FormGroup,
|
FormGroup,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
} from '@angular/forms'
|
} from '@angular/forms'
|
||||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
|
||||||
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
|
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
|
||||||
import { DocumentType } from 'src/app/data/document-type'
|
import { DocumentType } from 'src/app/data/document-type'
|
||||||
import { DEFAULT_MATCHING_ALGORITHM } from 'src/app/data/matching-model'
|
import { DEFAULT_MATCHING_ALGORITHM } from 'src/app/data/matching-model'
|
||||||
@@ -13,6 +12,7 @@ import { IfOwnerDirective } from 'src/app/directives/if-owner.directive'
|
|||||||
import { DocumentTypeService } from 'src/app/services/rest/document-type.service'
|
import { DocumentTypeService } from 'src/app/services/rest/document-type.service'
|
||||||
import { UserService } from 'src/app/services/rest/user.service'
|
import { UserService } from 'src/app/services/rest/user.service'
|
||||||
import { SettingsService } from 'src/app/services/settings.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 { PermissionsFormComponent } from '../../input/permissions/permissions-form/permissions-form.component'
|
||||||
import { SelectComponent } from '../../input/select/select.component'
|
import { SelectComponent } from '../../input/select/select.component'
|
||||||
import { TextComponent } from '../../input/text/text.component'
|
import { TextComponent } from '../../input/text/text.component'
|
||||||
@@ -22,6 +22,7 @@ import { TextComponent } from '../../input/text/text.component'
|
|||||||
templateUrl: './document-type-edit-dialog.component.html',
|
templateUrl: './document-type-edit-dialog.component.html',
|
||||||
styleUrls: ['./document-type-edit-dialog.component.scss'],
|
styleUrls: ['./document-type-edit-dialog.component.scss'],
|
||||||
imports: [
|
imports: [
|
||||||
|
CheckComponent,
|
||||||
SelectComponent,
|
SelectComponent,
|
||||||
PermissionsFormComponent,
|
PermissionsFormComponent,
|
||||||
TextComponent,
|
TextComponent,
|
||||||
@@ -31,13 +32,11 @@ import { TextComponent } from '../../input/text/text.component'
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class DocumentTypeEditDialogComponent extends EditDialogComponent<DocumentType> {
|
export class DocumentTypeEditDialogComponent extends EditDialogComponent<DocumentType> {
|
||||||
constructor(
|
constructor() {
|
||||||
service: DocumentTypeService,
|
super()
|
||||||
activeModal: NgbActiveModal,
|
this.service = inject(DocumentTypeService)
|
||||||
userService: UserService,
|
this.userService = inject(UserService)
|
||||||
settingsService: SettingsService
|
this.settingsService = inject(SettingsService)
|
||||||
) {
|
|
||||||
super(service, activeModal, userService, settingsService)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getCreateTitle() {
|
getCreateTitle() {
|
||||||
|
@@ -41,13 +41,9 @@ import { EditDialogComponent, EditDialogMode } from './edit-dialog.component'
|
|||||||
imports: [FormsModule, ReactiveFormsModule],
|
imports: [FormsModule, ReactiveFormsModule],
|
||||||
})
|
})
|
||||||
class TestComponent extends EditDialogComponent<Tag> {
|
class TestComponent extends EditDialogComponent<Tag> {
|
||||||
constructor(
|
constructor() {
|
||||||
service: TagService,
|
super()
|
||||||
activeModal: NgbActiveModal,
|
this.service = TestBed.inject(TagService)
|
||||||
userService: UserService,
|
|
||||||
settingsService: SettingsService
|
|
||||||
) {
|
|
||||||
super(service, activeModal, userService, settingsService)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getForm(): FormGroup<any> {
|
getForm(): FormGroup<any> {
|
||||||
|
@@ -1,4 +1,11 @@
|
|||||||
import { Directive, EventEmitter, Input, OnInit, Output } from '@angular/core'
|
import {
|
||||||
|
Directive,
|
||||||
|
EventEmitter,
|
||||||
|
Input,
|
||||||
|
OnInit,
|
||||||
|
Output,
|
||||||
|
inject,
|
||||||
|
} from '@angular/core'
|
||||||
import { FormGroup } from '@angular/forms'
|
import { FormGroup } from '@angular/forms'
|
||||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { Observable } from 'rxjs'
|
import { Observable } from 'rxjs'
|
||||||
@@ -29,14 +36,12 @@ export abstract class EditDialogComponent<
|
|||||||
extends LoadingComponentWithPermissions
|
extends LoadingComponentWithPermissions
|
||||||
implements OnInit
|
implements OnInit
|
||||||
{
|
{
|
||||||
constructor(
|
protected service = inject<AbstractPaperlessService<T>>(
|
||||||
protected service: AbstractPaperlessService<T>,
|
AbstractPaperlessService
|
||||||
private activeModal: NgbActiveModal,
|
)
|
||||||
private userService: UserService,
|
protected activeModal = inject(NgbActiveModal)
|
||||||
protected settingsService: SettingsService
|
protected userService = inject(UserService)
|
||||||
) {
|
protected settingsService = inject(SettingsService)
|
||||||
super()
|
|
||||||
}
|
|
||||||
|
|
||||||
users: User[]
|
users: User[]
|
||||||
|
|
||||||
|
@@ -1,11 +1,10 @@
|
|||||||
import { Component } from '@angular/core'
|
import { Component, inject } from '@angular/core'
|
||||||
import {
|
import {
|
||||||
FormControl,
|
FormControl,
|
||||||
FormGroup,
|
FormGroup,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
} from '@angular/forms'
|
} from '@angular/forms'
|
||||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
|
||||||
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
|
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
|
||||||
import { Group } from 'src/app/data/group'
|
import { Group } from 'src/app/data/group'
|
||||||
import { GroupService } from 'src/app/services/rest/group.service'
|
import { GroupService } from 'src/app/services/rest/group.service'
|
||||||
@@ -26,13 +25,11 @@ import { PermissionsSelectComponent } from '../../permissions-select/permissions
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class GroupEditDialogComponent extends EditDialogComponent<Group> {
|
export class GroupEditDialogComponent extends EditDialogComponent<Group> {
|
||||||
constructor(
|
constructor() {
|
||||||
service: GroupService,
|
super()
|
||||||
activeModal: NgbActiveModal,
|
this.service = inject(GroupService)
|
||||||
userService: UserService,
|
this.userService = inject(UserService)
|
||||||
settingsService: SettingsService
|
this.settingsService = inject(SettingsService)
|
||||||
) {
|
|
||||||
super(service, activeModal, userService, settingsService)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getCreateTitle() {
|
getCreateTitle() {
|
||||||
@@ -46,7 +43,7 @@ export class GroupEditDialogComponent extends EditDialogComponent<Group> {
|
|||||||
getForm(): FormGroup {
|
getForm(): FormGroup {
|
||||||
return new FormGroup({
|
return new FormGroup({
|
||||||
name: new FormControl(''),
|
name: new FormControl(''),
|
||||||
permissions: new FormControl(null),
|
permissions: new FormControl([]),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,15 +1,11 @@
|
|||||||
import { Component, ViewChild } from '@angular/core'
|
import { Component, ViewChild, inject } from '@angular/core'
|
||||||
import {
|
import {
|
||||||
FormControl,
|
FormControl,
|
||||||
FormGroup,
|
FormGroup,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
} from '@angular/forms'
|
} from '@angular/forms'
|
||||||
import {
|
import { NgbAlert, NgbAlertModule } from '@ng-bootstrap/ng-bootstrap'
|
||||||
NgbActiveModal,
|
|
||||||
NgbAlert,
|
|
||||||
NgbAlertModule,
|
|
||||||
} from '@ng-bootstrap/ng-bootstrap'
|
|
||||||
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
|
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
|
||||||
import { IMAPSecurity, MailAccount } from 'src/app/data/mail-account'
|
import { IMAPSecurity, MailAccount } from 'src/app/data/mail-account'
|
||||||
import { MailAccountService } from 'src/app/services/rest/mail-account.service'
|
import { MailAccountService } from 'src/app/services/rest/mail-account.service'
|
||||||
@@ -47,13 +43,11 @@ export class MailAccountEditDialogComponent extends EditDialogComponent<MailAcco
|
|||||||
|
|
||||||
@ViewChild('testResultAlert', { static: false }) testResultAlert: NgbAlert
|
@ViewChild('testResultAlert', { static: false }) testResultAlert: NgbAlert
|
||||||
|
|
||||||
constructor(
|
constructor() {
|
||||||
service: MailAccountService,
|
super()
|
||||||
activeModal: NgbActiveModal,
|
this.service = inject(MailAccountService)
|
||||||
userService: UserService,
|
this.userService = inject(UserService)
|
||||||
settingsService: SettingsService
|
this.settingsService = inject(SettingsService)
|
||||||
) {
|
|
||||||
super(service, activeModal, userService, settingsService)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getCreateTitle() {
|
getCreateTitle() {
|
||||||
|
@@ -1,11 +1,10 @@
|
|||||||
import { Component } from '@angular/core'
|
import { Component, inject } from '@angular/core'
|
||||||
import {
|
import {
|
||||||
FormControl,
|
FormControl,
|
||||||
FormGroup,
|
FormGroup,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
} from '@angular/forms'
|
} from '@angular/forms'
|
||||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
|
||||||
import { first } from 'rxjs'
|
import { first } from 'rxjs'
|
||||||
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
|
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
|
||||||
import { Correspondent } from 'src/app/data/correspondent'
|
import { Correspondent } from 'src/app/data/correspondent'
|
||||||
@@ -155,32 +154,34 @@ const METADATA_CORRESPONDENT_OPTIONS = [
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class MailRuleEditDialogComponent extends EditDialogComponent<MailRule> {
|
export class MailRuleEditDialogComponent extends EditDialogComponent<MailRule> {
|
||||||
|
private accountService: MailAccountService
|
||||||
|
private correspondentService: CorrespondentService
|
||||||
|
private documentTypeService: DocumentTypeService
|
||||||
|
|
||||||
accounts: MailAccount[]
|
accounts: MailAccount[]
|
||||||
correspondents: Correspondent[]
|
correspondents: Correspondent[]
|
||||||
documentTypes: DocumentType[]
|
documentTypes: DocumentType[]
|
||||||
|
|
||||||
constructor(
|
constructor() {
|
||||||
service: MailRuleService,
|
super()
|
||||||
activeModal: NgbActiveModal,
|
this.service = inject(MailRuleService)
|
||||||
accountService: MailAccountService,
|
this.accountService = inject(MailAccountService)
|
||||||
correspondentService: CorrespondentService,
|
this.correspondentService = inject(CorrespondentService)
|
||||||
documentTypeService: DocumentTypeService,
|
this.documentTypeService = inject(DocumentTypeService)
|
||||||
userService: UserService,
|
this.userService = inject(UserService)
|
||||||
settingsService: SettingsService
|
this.settingsService = inject(SettingsService)
|
||||||
) {
|
|
||||||
super(service, activeModal, userService, settingsService)
|
|
||||||
|
|
||||||
accountService
|
this.accountService
|
||||||
.listAll()
|
.listAll()
|
||||||
.pipe(first())
|
.pipe(first())
|
||||||
.subscribe((result) => (this.accounts = result.results))
|
.subscribe((result) => (this.accounts = result.results))
|
||||||
|
|
||||||
correspondentService
|
this.correspondentService
|
||||||
.listAll()
|
.listAll()
|
||||||
.pipe(first())
|
.pipe(first())
|
||||||
.subscribe((result) => (this.correspondents = result.results))
|
.subscribe((result) => (this.correspondents = result.results))
|
||||||
|
|
||||||
documentTypeService
|
this.documentTypeService
|
||||||
.listAll()
|
.listAll()
|
||||||
.pipe(first())
|
.pipe(first())
|
||||||
.subscribe((result) => (this.documentTypes = result.results))
|
.subscribe((result) => (this.documentTypes = result.results))
|
||||||
|
@@ -64,8 +64,6 @@
|
|||||||
<pngx-input-select i18n-title title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></pngx-input-select>
|
<pngx-input-select i18n-title title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></pngx-input-select>
|
||||||
@if (patternRequired) {
|
@if (patternRequired) {
|
||||||
<pngx-input-text i18n-title title="Matching pattern" formControlName="match" [error]="error?.match"></pngx-input-text>
|
<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>
|
<pngx-input-check i18n-title title="Case insensitive" formControlName="is_insensitive"></pngx-input-check>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,12 +1,12 @@
|
|||||||
import { AsyncPipe, NgTemplateOutlet } from '@angular/common'
|
import { AsyncPipe, NgTemplateOutlet } from '@angular/common'
|
||||||
import { Component, OnDestroy } from '@angular/core'
|
import { Component, OnDestroy, inject } from '@angular/core'
|
||||||
import {
|
import {
|
||||||
FormControl,
|
FormControl,
|
||||||
FormGroup,
|
FormGroup,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
} from '@angular/forms'
|
} from '@angular/forms'
|
||||||
import { NgbAccordionModule, NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { NgSelectComponent } from '@ng-select/ng-select'
|
import { NgSelectComponent } from '@ng-select/ng-select'
|
||||||
import {
|
import {
|
||||||
Observable,
|
Observable,
|
||||||
@@ -60,6 +60,8 @@ export class StoragePathEditDialogComponent
|
|||||||
extends EditDialogComponent<StoragePath>
|
extends EditDialogComponent<StoragePath>
|
||||||
implements OnDestroy
|
implements OnDestroy
|
||||||
{
|
{
|
||||||
|
private documentsService = inject(DocumentService)
|
||||||
|
|
||||||
public documentsInput$ = new Subject<string>()
|
public documentsInput$ = new Subject<string>()
|
||||||
public foundDocuments$: Observable<Document[]>
|
public foundDocuments$: Observable<Document[]>
|
||||||
private testDocument: Document
|
private testDocument: Document
|
||||||
@@ -68,14 +70,11 @@ export class StoragePathEditDialogComponent
|
|||||||
public loading = false
|
public loading = false
|
||||||
public testLoading = false
|
public testLoading = false
|
||||||
|
|
||||||
constructor(
|
constructor() {
|
||||||
service: StoragePathService,
|
super()
|
||||||
activeModal: NgbActiveModal,
|
this.service = inject(StoragePathService)
|
||||||
userService: UserService,
|
this.userService = inject(UserService)
|
||||||
settingsService: SettingsService,
|
this.settingsService = inject(SettingsService)
|
||||||
private documentsService: DocumentService
|
|
||||||
) {
|
|
||||||
super(service, activeModal, userService, settingsService)
|
|
||||||
this.initPathObservables()
|
this.initPathObservables()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -16,8 +16,6 @@
|
|||||||
<pngx-input-select i18n-title title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></pngx-input-select>
|
<pngx-input-select i18n-title title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></pngx-input-select>
|
||||||
@if (patternRequired) {
|
@if (patternRequired) {
|
||||||
<pngx-input-text i18n-title title="Matching pattern" formControlName="match" [error]="error?.match"></pngx-input-text>
|
<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>
|
<pngx-input-check i18n-title title="Case insensitive" formControlName="is_insensitive"></pngx-input-check>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,11 +1,10 @@
|
|||||||
import { Component } from '@angular/core'
|
import { Component, inject } from '@angular/core'
|
||||||
import {
|
import {
|
||||||
FormControl,
|
FormControl,
|
||||||
FormGroup,
|
FormGroup,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
} from '@angular/forms'
|
} from '@angular/forms'
|
||||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
|
||||||
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
|
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
|
||||||
import { DEFAULT_MATCHING_ALGORITHM } from 'src/app/data/matching-model'
|
import { DEFAULT_MATCHING_ALGORITHM } from 'src/app/data/matching-model'
|
||||||
import { Tag } from 'src/app/data/tag'
|
import { Tag } from 'src/app/data/tag'
|
||||||
@@ -36,13 +35,11 @@ import { TextComponent } from '../../input/text/text.component'
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class TagEditDialogComponent extends EditDialogComponent<Tag> {
|
export class TagEditDialogComponent extends EditDialogComponent<Tag> {
|
||||||
constructor(
|
constructor() {
|
||||||
service: TagService,
|
super()
|
||||||
activeModal: NgbActiveModal,
|
this.service = inject(TagService)
|
||||||
userService: UserService,
|
this.userService = inject(UserService)
|
||||||
settingsService: SettingsService
|
this.settingsService = inject(SettingsService)
|
||||||
) {
|
|
||||||
super(service, activeModal, userService, settingsService)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getCreateTitle() {
|
getCreateTitle() {
|
||||||
|
@@ -1,11 +1,10 @@
|
|||||||
import { Component, OnInit } from '@angular/core'
|
import { Component, OnInit, inject } from '@angular/core'
|
||||||
import {
|
import {
|
||||||
FormControl,
|
FormControl,
|
||||||
FormGroup,
|
FormGroup,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
} from '@angular/forms'
|
} from '@angular/forms'
|
||||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
|
||||||
import { first } from 'rxjs'
|
import { first } from 'rxjs'
|
||||||
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
|
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
|
||||||
import { Group } from 'src/app/data/group'
|
import { Group } from 'src/app/data/group'
|
||||||
@@ -37,21 +36,21 @@ export class UserEditDialogComponent
|
|||||||
extends EditDialogComponent<User>
|
extends EditDialogComponent<User>
|
||||||
implements OnInit
|
implements OnInit
|
||||||
{
|
{
|
||||||
|
private toastService = inject(ToastService)
|
||||||
|
private permissionsService = inject(PermissionsService)
|
||||||
|
private groupsService: GroupService
|
||||||
|
|
||||||
groups: Group[]
|
groups: Group[]
|
||||||
passwordIsSet: boolean = false
|
passwordIsSet: boolean = false
|
||||||
public totpLoading: boolean = false
|
public totpLoading: boolean = false
|
||||||
|
|
||||||
constructor(
|
constructor() {
|
||||||
service: UserService,
|
super()
|
||||||
activeModal: NgbActiveModal,
|
this.service = inject(UserService)
|
||||||
groupsService: GroupService,
|
this.groupsService = inject(GroupService)
|
||||||
settingsService: SettingsService,
|
this.settingsService = inject(SettingsService)
|
||||||
private toastService: ToastService,
|
|
||||||
private permissionsService: PermissionsService
|
|
||||||
) {
|
|
||||||
super(service, activeModal, service, settingsService)
|
|
||||||
|
|
||||||
groupsService
|
this.groupsService
|
||||||
.listAll()
|
.listAll()
|
||||||
.pipe(first())
|
.pipe(first())
|
||||||
.subscribe((result) => (this.groups = result.results))
|
.subscribe((result) => (this.groups = result.results))
|
||||||
|
@@ -4,7 +4,7 @@ import {
|
|||||||
moveItemInArray,
|
moveItemInArray,
|
||||||
} from '@angular/cdk/drag-drop'
|
} from '@angular/cdk/drag-drop'
|
||||||
import { NgTemplateOutlet } from '@angular/common'
|
import { NgTemplateOutlet } from '@angular/common'
|
||||||
import { Component, OnInit } from '@angular/core'
|
import { Component, OnInit, inject } from '@angular/core'
|
||||||
import {
|
import {
|
||||||
FormArray,
|
FormArray,
|
||||||
FormControl,
|
FormControl,
|
||||||
@@ -12,7 +12,7 @@ import {
|
|||||||
FormsModule,
|
FormsModule,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
} from '@angular/forms'
|
} from '@angular/forms'
|
||||||
import { NgbAccordionModule, NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
||||||
import { first } from 'rxjs'
|
import { first } from 'rxjs'
|
||||||
import { Correspondent } from 'src/app/data/correspondent'
|
import { Correspondent } from 'src/app/data/correspondent'
|
||||||
@@ -171,6 +171,12 @@ export class WorkflowEditDialogComponent
|
|||||||
public WorkflowTriggerType = WorkflowTriggerType
|
public WorkflowTriggerType = WorkflowTriggerType
|
||||||
public WorkflowActionType = WorkflowActionType
|
public WorkflowActionType = WorkflowActionType
|
||||||
|
|
||||||
|
private correspondentService: CorrespondentService
|
||||||
|
private documentTypeService: DocumentTypeService
|
||||||
|
private storagePathService: StoragePathService
|
||||||
|
private mailRuleService: MailRuleService
|
||||||
|
private customFieldsService: CustomFieldsService
|
||||||
|
|
||||||
templates: Workflow[]
|
templates: Workflow[]
|
||||||
correspondents: Correspondent[]
|
correspondents: Correspondent[]
|
||||||
documentTypes: DocumentType[]
|
documentTypes: DocumentType[]
|
||||||
@@ -183,40 +189,38 @@ export class WorkflowEditDialogComponent
|
|||||||
|
|
||||||
private allowedActionTypes = []
|
private allowedActionTypes = []
|
||||||
|
|
||||||
constructor(
|
constructor() {
|
||||||
service: WorkflowService,
|
super()
|
||||||
activeModal: NgbActiveModal,
|
this.service = inject(WorkflowService)
|
||||||
correspondentService: CorrespondentService,
|
this.correspondentService = inject(CorrespondentService)
|
||||||
documentTypeService: DocumentTypeService,
|
this.documentTypeService = inject(DocumentTypeService)
|
||||||
storagePathService: StoragePathService,
|
this.storagePathService = inject(StoragePathService)
|
||||||
mailRuleService: MailRuleService,
|
this.mailRuleService = inject(MailRuleService)
|
||||||
userService: UserService,
|
this.userService = inject(UserService)
|
||||||
settingsService: SettingsService,
|
this.settingsService = inject(SettingsService)
|
||||||
customFieldsService: CustomFieldsService
|
this.customFieldsService = inject(CustomFieldsService)
|
||||||
) {
|
|
||||||
super(service, activeModal, userService, settingsService)
|
|
||||||
|
|
||||||
correspondentService
|
this.correspondentService
|
||||||
.listAll()
|
.listAll()
|
||||||
.pipe(first())
|
.pipe(first())
|
||||||
.subscribe((result) => (this.correspondents = result.results))
|
.subscribe((result) => (this.correspondents = result.results))
|
||||||
|
|
||||||
documentTypeService
|
this.documentTypeService
|
||||||
.listAll()
|
.listAll()
|
||||||
.pipe(first())
|
.pipe(first())
|
||||||
.subscribe((result) => (this.documentTypes = result.results))
|
.subscribe((result) => (this.documentTypes = result.results))
|
||||||
|
|
||||||
storagePathService
|
this.storagePathService
|
||||||
.listAll()
|
.listAll()
|
||||||
.pipe(first())
|
.pipe(first())
|
||||||
.subscribe((result) => (this.storagePaths = result.results))
|
.subscribe((result) => (this.storagePaths = result.results))
|
||||||
|
|
||||||
mailRuleService
|
this.mailRuleService
|
||||||
.listAll()
|
.listAll()
|
||||||
.pipe(first())
|
.pipe(first())
|
||||||
.subscribe((result) => (this.mailRules = result.results))
|
.subscribe((result) => (this.mailRules = result.results))
|
||||||
|
|
||||||
customFieldsService
|
this.customFieldsService
|
||||||
.listAll()
|
.listAll()
|
||||||
.pipe(first())
|
.pipe(first())
|
||||||
.subscribe((result) => {
|
.subscribe((result) => {
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { Component, Input } from '@angular/core'
|
import { Component, Input, inject } from '@angular/core'
|
||||||
import { FormsModule } from '@angular/forms'
|
import { FormsModule } from '@angular/forms'
|
||||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
||||||
@@ -13,6 +13,10 @@ import { LoadingComponentWithPermissions } from '../../loading-component/loading
|
|||||||
imports: [FormsModule, NgxBootstrapIconsModule],
|
imports: [FormsModule, NgxBootstrapIconsModule],
|
||||||
})
|
})
|
||||||
export class EmailDocumentDialogComponent extends LoadingComponentWithPermissions {
|
export class EmailDocumentDialogComponent extends LoadingComponentWithPermissions {
|
||||||
|
private activeModal = inject(NgbActiveModal)
|
||||||
|
private documentService = inject(DocumentService)
|
||||||
|
private toastService = inject(ToastService)
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
title = $localize`Email Document`
|
title = $localize`Email Document`
|
||||||
|
|
||||||
@@ -37,11 +41,7 @@ export class EmailDocumentDialogComponent extends LoadingComponentWithPermission
|
|||||||
public emailSubject: string = ''
|
public emailSubject: string = ''
|
||||||
public emailMessage: string = ''
|
public emailMessage: string = ''
|
||||||
|
|
||||||
constructor(
|
constructor() {
|
||||||
private activeModal: NgbActiveModal,
|
|
||||||
private documentService: DocumentService,
|
|
||||||
private toastService: ToastService
|
|
||||||
) {
|
|
||||||
super()
|
super()
|
||||||
this.loading = false
|
this.loading = false
|
||||||
}
|
}
|
||||||
|
@@ -30,7 +30,7 @@
|
|||||||
}
|
}
|
||||||
<div class="list-group-item">
|
<div class="list-group-item">
|
||||||
<div class="input-group input-group-sm">
|
<div class="input-group input-group-sm">
|
||||||
<input class="form-control" type="text" [(ngModel)]="filterText" [placeholder]="filterPlaceholder" (keyup.enter)="listFilterEnter()" #listFilterTextInput>
|
<input class="form-control" type="text" spellcheck="false" [(ngModel)]="filterText" [placeholder]="filterPlaceholder" (keyup.enter)="listFilterEnter()" #listFilterTextInput>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@if (selectionModel.items) {
|
@if (selectionModel.items) {
|
||||||
|
@@ -7,6 +7,7 @@ import {
|
|||||||
OnInit,
|
OnInit,
|
||||||
Output,
|
Output,
|
||||||
ViewChild,
|
ViewChild,
|
||||||
|
inject,
|
||||||
} from '@angular/core'
|
} from '@angular/core'
|
||||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||||
import { NgbDropdown, NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbDropdown, NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'
|
||||||
@@ -434,6 +435,9 @@ export class FilterableDropdownComponent
|
|||||||
extends LoadingComponentWithPermissions
|
extends LoadingComponentWithPermissions
|
||||||
implements OnInit
|
implements OnInit
|
||||||
{
|
{
|
||||||
|
private filterPipe = inject(FilterPipe)
|
||||||
|
private hotkeyService = inject(HotKeyService)
|
||||||
|
|
||||||
@ViewChild('listFilterTextInput') listFilterTextInput: ElementRef
|
@ViewChild('listFilterTextInput') listFilterTextInput: ElementRef
|
||||||
@ViewChild('dropdown') dropdown: NgbDropdown
|
@ViewChild('dropdown') dropdown: NgbDropdown
|
||||||
@ViewChild('buttonItems') buttonItems: ElementRef
|
@ViewChild('buttonItems') buttonItems: ElementRef
|
||||||
@@ -536,10 +540,7 @@ export class FilterableDropdownComponent
|
|||||||
|
|
||||||
private keyboardIndex: number
|
private keyboardIndex: number
|
||||||
|
|
||||||
constructor(
|
constructor() {
|
||||||
private filterPipe: FilterPipe,
|
|
||||||
private hotkeyService: HotKeyService
|
|
||||||
) {
|
|
||||||
super()
|
super()
|
||||||
this.selectionModelChange.subscribe((updatedModel) => {
|
this.selectionModelChange.subscribe((updatedModel) => {
|
||||||
this.modelIsDirty = updatedModel.isDirty()
|
this.modelIsDirty = updatedModel.isDirty()
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { Component } from '@angular/core'
|
import { Component, inject } from '@angular/core'
|
||||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
|
|
||||||
const SYMBOLS = {
|
const SYMBOLS = {
|
||||||
@@ -19,11 +19,11 @@ const SYMBOLS = {
|
|||||||
styleUrl: './hotkey-dialog.component.scss',
|
styleUrl: './hotkey-dialog.component.scss',
|
||||||
})
|
})
|
||||||
export class HotkeyDialogComponent {
|
export class HotkeyDialogComponent {
|
||||||
|
activeModal = inject(NgbActiveModal)
|
||||||
|
|
||||||
public title: string = $localize`Keyboard shortcuts`
|
public title: string = $localize`Keyboard shortcuts`
|
||||||
public hotkeys: Map<string, string> = new Map()
|
public hotkeys: Map<string, string> = new Map()
|
||||||
|
|
||||||
constructor(public activeModal: NgbActiveModal) {}
|
|
||||||
|
|
||||||
public close(): void {
|
public close(): void {
|
||||||
this.activeModal.close()
|
this.activeModal.close()
|
||||||
}
|
}
|
||||||
|
@@ -2,6 +2,7 @@ import {
|
|||||||
Component,
|
Component,
|
||||||
EventEmitter,
|
EventEmitter,
|
||||||
forwardRef,
|
forwardRef,
|
||||||
|
inject,
|
||||||
Input,
|
Input,
|
||||||
Output,
|
Output,
|
||||||
} from '@angular/core'
|
} from '@angular/core'
|
||||||
@@ -55,7 +56,9 @@ import { UrlComponent } from '../url/url.component'
|
|||||||
export class CustomFieldsValuesComponent extends AbstractInputComponent<Object> {
|
export class CustomFieldsValuesComponent extends AbstractInputComponent<Object> {
|
||||||
public CustomFieldDataType = CustomFieldDataType
|
public CustomFieldDataType = CustomFieldDataType
|
||||||
|
|
||||||
constructor(customFieldsService: CustomFieldsService) {
|
constructor() {
|
||||||
|
const customFieldsService = inject(CustomFieldsService)
|
||||||
|
|
||||||
super()
|
super()
|
||||||
customFieldsService.listAll().subscribe((items) => {
|
customFieldsService.listAll().subscribe((items) => {
|
||||||
this.fields = items.results
|
this.fields = items.results
|
||||||
|
@@ -2,6 +2,7 @@ import {
|
|||||||
Component,
|
Component,
|
||||||
EventEmitter,
|
EventEmitter,
|
||||||
forwardRef,
|
forwardRef,
|
||||||
|
inject,
|
||||||
Input,
|
Input,
|
||||||
OnInit,
|
OnInit,
|
||||||
Output,
|
Output,
|
||||||
@@ -45,13 +46,9 @@ export class DateComponent
|
|||||||
extends AbstractInputComponent<string>
|
extends AbstractInputComponent<string>
|
||||||
implements OnInit
|
implements OnInit
|
||||||
{
|
{
|
||||||
constructor(
|
private settings = inject(SettingsService)
|
||||||
private settings: SettingsService,
|
private ngbDateParserFormatter = inject(NgbDateParserFormatter)
|
||||||
private ngbDateParserFormatter: NgbDateParserFormatter,
|
private isoDateAdapter = inject<NgbDateAdapter<string>>(NgbDateAdapter)
|
||||||
private isoDateAdapter: NgbDateAdapter<string>
|
|
||||||
) {
|
|
||||||
super()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
suggestions: string[]
|
suggestions: string[]
|
||||||
@@ -62,7 +59,7 @@ export class DateComponent
|
|||||||
@Output()
|
@Output()
|
||||||
filterDocuments = new EventEmitter<NgbDateStruct[]>()
|
filterDocuments = new EventEmitter<NgbDateStruct[]>()
|
||||||
|
|
||||||
public readonly today: string = new Date().toISOString().split('T')[0]
|
public readonly today: string = new Date().toLocaleDateString('en-CA')
|
||||||
|
|
||||||
getSuggestions() {
|
getSuggestions() {
|
||||||
return this.suggestions == null
|
return this.suggestions == null
|
||||||
|
@@ -1,5 +1,12 @@
|
|||||||
import { AsyncPipe, NgTemplateOutlet } from '@angular/common'
|
import { AsyncPipe, NgTemplateOutlet } from '@angular/common'
|
||||||
import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core'
|
import {
|
||||||
|
Component,
|
||||||
|
forwardRef,
|
||||||
|
inject,
|
||||||
|
Input,
|
||||||
|
OnDestroy,
|
||||||
|
OnInit,
|
||||||
|
} from '@angular/core'
|
||||||
import {
|
import {
|
||||||
FormsModule,
|
FormsModule,
|
||||||
NG_VALUE_ACCESSOR,
|
NG_VALUE_ACCESSOR,
|
||||||
@@ -52,6 +59,8 @@ export class DocumentLinkComponent
|
|||||||
extends AbstractInputComponent<any[]>
|
extends AbstractInputComponent<any[]>
|
||||||
implements OnInit, OnDestroy
|
implements OnInit, OnDestroy
|
||||||
{
|
{
|
||||||
|
private documentsService = inject(DocumentService)
|
||||||
|
|
||||||
documentsInput$ = new Subject<string>()
|
documentsInput$ = new Subject<string>()
|
||||||
foundDocuments$: Observable<Document[]>
|
foundDocuments$: Observable<Document[]>
|
||||||
loading = false
|
loading = false
|
||||||
@@ -75,10 +84,6 @@ export class DocumentLinkComponent
|
|||||||
return this.selectedDocuments.map((d) => d.id)
|
return this.selectedDocuments.map((d) => d.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(private documentsService: DocumentService) {
|
|
||||||
super()
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.loadDocs()
|
this.loadDocs()
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
|
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
|
||||||
import { provideHttpClientTesting } from '@angular/common/http/testing'
|
import { provideHttpClientTesting } from '@angular/common/http/testing'
|
||||||
|
import { LOCALE_ID } from '@angular/core'
|
||||||
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||||
import { NG_VALUE_ACCESSOR } from '@angular/forms'
|
import { NG_VALUE_ACCESSOR } from '@angular/forms'
|
||||||
import { MonetaryComponent } from './monetary.component'
|
import { MonetaryComponent } from './monetary.component'
|
||||||
@@ -41,8 +42,6 @@ describe('MonetaryComponent', () => {
|
|||||||
|
|
||||||
it('should set the default currency code based on LOCALE_ID', () => {
|
it('should set the default currency code based on LOCALE_ID', () => {
|
||||||
expect(component.defaultCurrencyCode).toEqual('USD') // default
|
expect(component.defaultCurrencyCode).toEqual('USD') // default
|
||||||
component = new MonetaryComponent('pt-BR')
|
|
||||||
expect(component.defaultCurrencyCode).toEqual('BRL')
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should support setting a default currency code', () => {
|
it('should support setting a default currency code', () => {
|
||||||
@@ -87,3 +86,28 @@ describe('MonetaryComponent', () => {
|
|||||||
expect(component.value).toEqual('USD0.00')
|
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')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { CurrencyPipe, getLocaleCurrencyCode } from '@angular/common'
|
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 {
|
import {
|
||||||
FormsModule,
|
FormsModule,
|
||||||
NG_VALUE_ACCESSOR,
|
NG_VALUE_ACCESSOR,
|
||||||
@@ -27,6 +27,8 @@ import { AbstractInputComponent } from '../abstract-input'
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class MonetaryComponent extends AbstractInputComponent<string> {
|
export class MonetaryComponent extends AbstractInputComponent<string> {
|
||||||
|
currentLocale = inject(LOCALE_ID)
|
||||||
|
|
||||||
public currency: string = ''
|
public currency: string = ''
|
||||||
|
|
||||||
public _monetaryValue: string = ''
|
public _monetaryValue: string = ''
|
||||||
@@ -45,11 +47,10 @@ export class MonetaryComponent extends AbstractInputComponent<string> {
|
|||||||
if (currency) this.defaultCurrencyCode = currency
|
if (currency) this.defaultCurrencyCode = currency
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(@Inject(LOCALE_ID) currentLocale: string) {
|
constructor() {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
this.currency = this.defaultCurrencyCode =
|
this.currency = this.defaultCurrencyCode =
|
||||||
this.defaultCurrency ?? getLocaleCurrencyCode(currentLocale)
|
this.defaultCurrency ?? getLocaleCurrencyCode(this.currentLocale)
|
||||||
}
|
}
|
||||||
|
|
||||||
writeValue(newValue: any): void {
|
writeValue(newValue: any): void {
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { Component, forwardRef, Input } from '@angular/core'
|
import { Component, forwardRef, inject, Input } from '@angular/core'
|
||||||
import {
|
import {
|
||||||
FormsModule,
|
FormsModule,
|
||||||
NG_VALUE_ACCESSOR,
|
NG_VALUE_ACCESSOR,
|
||||||
@@ -22,16 +22,14 @@ import { AbstractInputComponent } from '../abstract-input'
|
|||||||
imports: [FormsModule, ReactiveFormsModule, NgxBootstrapIconsModule],
|
imports: [FormsModule, ReactiveFormsModule, NgxBootstrapIconsModule],
|
||||||
})
|
})
|
||||||
export class NumberComponent extends AbstractInputComponent<number> {
|
export class NumberComponent extends AbstractInputComponent<number> {
|
||||||
|
private documentService = inject(DocumentService)
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
showAdd: boolean = true
|
showAdd: boolean = true
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
step: number = 1
|
step: number = 1
|
||||||
|
|
||||||
constructor(private documentService: DocumentService) {
|
|
||||||
super()
|
|
||||||
}
|
|
||||||
|
|
||||||
nextAsn() {
|
nextAsn() {
|
||||||
if (this.value) {
|
if (this.value) {
|
||||||
return
|
return
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { Component, forwardRef } from '@angular/core'
|
import { Component, forwardRef, inject } from '@angular/core'
|
||||||
import {
|
import {
|
||||||
FormsModule,
|
FormsModule,
|
||||||
NG_VALUE_ACCESSOR,
|
NG_VALUE_ACCESSOR,
|
||||||
@@ -26,7 +26,9 @@ import { AbstractInputComponent } from '../../abstract-input'
|
|||||||
export class PermissionsGroupComponent extends AbstractInputComponent<Group> {
|
export class PermissionsGroupComponent extends AbstractInputComponent<Group> {
|
||||||
groups: Group[]
|
groups: Group[]
|
||||||
|
|
||||||
constructor(groupService: GroupService) {
|
constructor() {
|
||||||
|
const groupService = inject(GroupService)
|
||||||
|
|
||||||
super()
|
super()
|
||||||
groupService
|
groupService
|
||||||
.listAll()
|
.listAll()
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { Component, forwardRef } from '@angular/core'
|
import { Component, forwardRef, inject } from '@angular/core'
|
||||||
import {
|
import {
|
||||||
FormsModule,
|
FormsModule,
|
||||||
NG_VALUE_ACCESSOR,
|
NG_VALUE_ACCESSOR,
|
||||||
@@ -8,7 +8,6 @@ import { NgSelectComponent } from '@ng-select/ng-select'
|
|||||||
import { first } from 'rxjs/operators'
|
import { first } from 'rxjs/operators'
|
||||||
import { User } from 'src/app/data/user'
|
import { User } from 'src/app/data/user'
|
||||||
import { UserService } from 'src/app/services/rest/user.service'
|
import { UserService } from 'src/app/services/rest/user.service'
|
||||||
import { SettingsService } from 'src/app/services/settings.service'
|
|
||||||
import { AbstractInputComponent } from '../../abstract-input'
|
import { AbstractInputComponent } from '../../abstract-input'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -27,7 +26,9 @@ import { AbstractInputComponent } from '../../abstract-input'
|
|||||||
export class PermissionsUserComponent extends AbstractInputComponent<User[]> {
|
export class PermissionsUserComponent extends AbstractInputComponent<User[]> {
|
||||||
users: User[]
|
users: User[]
|
||||||
|
|
||||||
constructor(userService: UserService, settings: SettingsService) {
|
constructor() {
|
||||||
|
const userService = inject(UserService)
|
||||||
|
|
||||||
super()
|
super()
|
||||||
userService
|
userService
|
||||||
.listAll()
|
.listAll()
|
||||||
|
@@ -2,6 +2,7 @@ import {
|
|||||||
Component,
|
Component,
|
||||||
EventEmitter,
|
EventEmitter,
|
||||||
forwardRef,
|
forwardRef,
|
||||||
|
inject,
|
||||||
Input,
|
Input,
|
||||||
OnInit,
|
OnInit,
|
||||||
Output,
|
Output,
|
||||||
@@ -45,10 +46,10 @@ import { TagComponent } from '../../tag/tag.component'
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class TagsComponent implements OnInit, ControlValueAccessor {
|
export class TagsComponent implements OnInit, ControlValueAccessor {
|
||||||
constructor(
|
private tagService = inject(TagService)
|
||||||
private tagService: TagService,
|
private modalService = inject(NgbModal)
|
||||||
private modalService: NgbModal
|
|
||||||
) {
|
constructor() {
|
||||||
this.createTagRef = this.createTag.bind(this)
|
this.createTagRef = this.createTag.bind(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { Component, Input } from '@angular/core'
|
import { Component, Input, inject } from '@angular/core'
|
||||||
import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
|
import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
|
||||||
import { SettingsService } from 'src/app/services/settings.service'
|
import { SettingsService } from 'src/app/services/settings.service'
|
||||||
import { environment } from 'src/environments/environment'
|
import { environment } from 'src/environments/environment'
|
||||||
@@ -9,6 +9,8 @@ import { environment } from 'src/environments/environment'
|
|||||||
styleUrls: ['./logo.component.scss'],
|
styleUrls: ['./logo.component.scss'],
|
||||||
})
|
})
|
||||||
export class LogoComponent {
|
export class LogoComponent {
|
||||||
|
private settingsService = inject(SettingsService)
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
extra_classes: string
|
extra_classes: string
|
||||||
|
|
||||||
@@ -24,8 +26,6 @@ export class LogoComponent {
|
|||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(private settingsService: SettingsService) {}
|
|
||||||
|
|
||||||
getClasses() {
|
getClasses() {
|
||||||
return ['logo'].concat(this.extra_classes).join(' ')
|
return ['logo'].concat(this.extra_classes).join(' ')
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { Component, Input } from '@angular/core'
|
import { Component, Input, inject } from '@angular/core'
|
||||||
import { Title } from '@angular/platform-browser'
|
import { Title } from '@angular/platform-browser'
|
||||||
import { NgbPopoverModule } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbPopoverModule } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
||||||
@@ -12,7 +12,7 @@ import { environment } from 'src/environments/environment'
|
|||||||
imports: [NgbPopoverModule, NgxBootstrapIconsModule, TourNgBootstrapModule],
|
imports: [NgbPopoverModule, NgxBootstrapIconsModule, TourNgBootstrapModule],
|
||||||
})
|
})
|
||||||
export class PageHeaderComponent {
|
export class PageHeaderComponent {
|
||||||
constructor(private titleService: Title) {}
|
private titleService = inject(Title)
|
||||||
|
|
||||||
_title = ''
|
_title = ''
|
||||||
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { Component, EventEmitter, Input, Output } from '@angular/core'
|
import { Component, EventEmitter, Input, Output, inject } from '@angular/core'
|
||||||
import {
|
import {
|
||||||
FormControl,
|
FormControl,
|
||||||
FormGroup,
|
FormGroup,
|
||||||
@@ -24,13 +24,13 @@ import { SwitchComponent } from '../input/switch/switch.component'
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class PermissionsDialogComponent {
|
export class PermissionsDialogComponent {
|
||||||
|
activeModal = inject(NgbActiveModal)
|
||||||
|
private userService = inject(UserService)
|
||||||
|
|
||||||
users: User[]
|
users: User[]
|
||||||
private o: ObjectWithPermissions = undefined
|
private o: ObjectWithPermissions = undefined
|
||||||
|
|
||||||
constructor(
|
constructor() {
|
||||||
public activeModal: NgbActiveModal,
|
|
||||||
private userService: UserService
|
|
||||||
) {
|
|
||||||
this.userService.listAll().subscribe((r) => (this.users = r.results))
|
this.userService.listAll().subscribe((r) => (this.users = r.results))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { NgClass } from '@angular/common'
|
import { NgClass } from '@angular/common'
|
||||||
import { Component, EventEmitter, Input, Output } from '@angular/core'
|
import { Component, EventEmitter, Input, Output, inject } from '@angular/core'
|
||||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||||
import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { NgSelectComponent } from '@ng-select/ng-select'
|
import { NgSelectComponent } from '@ng-select/ng-select'
|
||||||
@@ -58,6 +58,9 @@ export enum OwnerFilterType {
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class PermissionsFilterDropdownComponent extends ComponentWithPermissions {
|
export class PermissionsFilterDropdownComponent extends ComponentWithPermissions {
|
||||||
|
permissionsService = inject(PermissionsService)
|
||||||
|
private settingsService = inject(SettingsService)
|
||||||
|
|
||||||
public OwnerFilterType = OwnerFilterType
|
public OwnerFilterType = OwnerFilterType
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
@@ -83,12 +86,12 @@ export class PermissionsFilterDropdownComponent extends ComponentWithPermissions
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor() {
|
||||||
public permissionsService: PermissionsService,
|
const userService = inject(UserService)
|
||||||
userService: UserService,
|
|
||||||
private settingsService: SettingsService
|
|
||||||
) {
|
|
||||||
super()
|
super()
|
||||||
|
const permissionsService = this.permissionsService
|
||||||
|
|
||||||
if (
|
if (
|
||||||
permissionsService.currentUserCan(
|
permissionsService.currentUserCan(
|
||||||
PermissionAction.View,
|
PermissionAction.View,
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { KeyValuePipe } from '@angular/common'
|
import { KeyValuePipe } from '@angular/common'
|
||||||
import { Component, forwardRef, Input, OnInit } from '@angular/core'
|
import { Component, forwardRef, inject, Input, OnInit } from '@angular/core'
|
||||||
import {
|
import {
|
||||||
AbstractControl,
|
AbstractControl,
|
||||||
ControlValueAccessor,
|
ControlValueAccessor,
|
||||||
@@ -43,6 +43,9 @@ export class PermissionsSelectComponent
|
|||||||
extends ComponentWithPermissions
|
extends ComponentWithPermissions
|
||||||
implements OnInit, ControlValueAccessor
|
implements OnInit, ControlValueAccessor
|
||||||
{
|
{
|
||||||
|
private readonly permissionsService = inject(PermissionsService)
|
||||||
|
private readonly settingsService = inject(SettingsService)
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
title: string = 'Permissions'
|
title: string = 'Permissions'
|
||||||
|
|
||||||
@@ -76,10 +79,7 @@ export class PermissionsSelectComponent
|
|||||||
|
|
||||||
public allowedTypes = Object.keys(PermissionType)
|
public allowedTypes = Object.keys(PermissionType)
|
||||||
|
|
||||||
constructor(
|
constructor() {
|
||||||
private readonly permissionsService: PermissionsService,
|
|
||||||
private readonly settingsService: SettingsService
|
|
||||||
) {
|
|
||||||
super()
|
super()
|
||||||
if (!this.settingsService.get(SETTINGS_KEYS.AUDITLOG_ENABLED)) {
|
if (!this.settingsService.get(SETTINGS_KEYS.AUDITLOG_ENABLED)) {
|
||||||
this.allowedTypes.splice(this.allowedTypes.indexOf('History'), 1)
|
this.allowedTypes.splice(this.allowedTypes.indexOf('History'), 1)
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { HttpClient } from '@angular/common/http'
|
import { HttpClient } from '@angular/common/http'
|
||||||
import { Component, Input, OnDestroy, ViewChild } from '@angular/core'
|
import { Component, inject, Input, OnDestroy, ViewChild } from '@angular/core'
|
||||||
import { NgbPopover, NgbPopoverModule } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbPopover, NgbPopoverModule } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { PdfViewerComponent, PdfViewerModule } from 'ng2-pdf-viewer'
|
import { PdfViewerComponent, PdfViewerModule } from 'ng2-pdf-viewer'
|
||||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
||||||
@@ -24,6 +24,10 @@ import { SettingsService } from 'src/app/services/settings.service'
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class PreviewPopupComponent implements OnDestroy {
|
export class PreviewPopupComponent implements OnDestroy {
|
||||||
|
private settingsService = inject(SettingsService)
|
||||||
|
private documentService = inject(DocumentService)
|
||||||
|
private http = inject(HttpClient)
|
||||||
|
|
||||||
private _document: Document
|
private _document: Document
|
||||||
@Input()
|
@Input()
|
||||||
set document(document: Document) {
|
set document(document: Document) {
|
||||||
@@ -82,12 +86,6 @@ export class PreviewPopupComponent implements OnDestroy {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
|
||||||
private settingsService: SettingsService,
|
|
||||||
private documentService: DocumentService,
|
|
||||||
private http: HttpClient
|
|
||||||
) {}
|
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
this.unsubscribeNotifier.next(this)
|
this.unsubscribeNotifier.next(this)
|
||||||
}
|
}
|
||||||
|
@@ -18,6 +18,7 @@ import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
|
|||||||
import { of, throwError } from 'rxjs'
|
import { of, throwError } from 'rxjs'
|
||||||
import { ProfileService } from 'src/app/services/profile.service'
|
import { ProfileService } from 'src/app/services/profile.service'
|
||||||
import { ToastService } from 'src/app/services/toast.service'
|
import { ToastService } from 'src/app/services/toast.service'
|
||||||
|
import * as navUtils from 'src/app/utils/navigation'
|
||||||
import { ConfirmButtonComponent } from '../confirm-button/confirm-button.component'
|
import { ConfirmButtonComponent } from '../confirm-button/confirm-button.component'
|
||||||
import { PasswordComponent } from '../input/password/password.component'
|
import { PasswordComponent } from '../input/password/password.component'
|
||||||
import { TextComponent } from '../input/text/text.component'
|
import { TextComponent } from '../input/text/text.component'
|
||||||
@@ -205,16 +206,15 @@ describe('ProfileEditDialogComponent', () => {
|
|||||||
|
|
||||||
const updateSpy = jest.spyOn(profileService, 'update')
|
const updateSpy = jest.spyOn(profileService, 'update')
|
||||||
updateSpy.mockReturnValue(of(null))
|
updateSpy.mockReturnValue(of(null))
|
||||||
Object.defineProperty(window, 'location', {
|
const navSpy = jest
|
||||||
value: {
|
.spyOn(navUtils, 'setLocationHref')
|
||||||
href: 'http://localhost/',
|
.mockImplementation(() => {})
|
||||||
},
|
|
||||||
writable: true, // possibility to override
|
|
||||||
})
|
|
||||||
component.save()
|
component.save()
|
||||||
expect(updateSpy).toHaveBeenCalled()
|
expect(updateSpy).toHaveBeenCalled()
|
||||||
tick(2600)
|
tick(2600)
|
||||||
expect(window.location.href).toContain('logout')
|
expect(navSpy).toHaveBeenCalledWith(
|
||||||
|
`${window.location.origin}/accounts/logout/?next=/accounts/login/?next=/`
|
||||||
|
)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
it('should support auth token copy', fakeAsync(() => {
|
it('should support auth token copy', fakeAsync(() => {
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { Clipboard } from '@angular/cdk/clipboard'
|
import { Clipboard } from '@angular/cdk/clipboard'
|
||||||
import { Component, OnInit } from '@angular/core'
|
import { Component, OnInit, inject } from '@angular/core'
|
||||||
import {
|
import {
|
||||||
FormControl,
|
FormControl,
|
||||||
FormGroup,
|
FormGroup,
|
||||||
@@ -21,6 +21,7 @@ import {
|
|||||||
import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe'
|
import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe'
|
||||||
import { ProfileService } from 'src/app/services/profile.service'
|
import { ProfileService } from 'src/app/services/profile.service'
|
||||||
import { ToastService } from 'src/app/services/toast.service'
|
import { ToastService } from 'src/app/services/toast.service'
|
||||||
|
import { setLocationHref } from 'src/app/utils/navigation'
|
||||||
import { LoadingComponentWithPermissions } from '../../loading-component/loading.component'
|
import { LoadingComponentWithPermissions } from '../../loading-component/loading.component'
|
||||||
import { ConfirmButtonComponent } from '../confirm-button/confirm-button.component'
|
import { ConfirmButtonComponent } from '../confirm-button/confirm-button.component'
|
||||||
import { PasswordComponent } from '../input/password/password.component'
|
import { PasswordComponent } from '../input/password/password.component'
|
||||||
@@ -46,6 +47,11 @@ export class ProfileEditDialogComponent
|
|||||||
extends LoadingComponentWithPermissions
|
extends LoadingComponentWithPermissions
|
||||||
implements OnInit
|
implements OnInit
|
||||||
{
|
{
|
||||||
|
private profileService = inject(ProfileService)
|
||||||
|
activeModal = inject(NgbActiveModal)
|
||||||
|
private toastService = inject(ToastService)
|
||||||
|
private clipboard = inject(Clipboard)
|
||||||
|
|
||||||
public networkActive: boolean = false
|
public networkActive: boolean = false
|
||||||
public error: any
|
public error: any
|
||||||
|
|
||||||
@@ -83,15 +89,6 @@ export class ProfileEditDialogComponent
|
|||||||
public socialAccounts: SocialAccount[] = []
|
public socialAccounts: SocialAccount[] = []
|
||||||
public socialAccountProviders: SocialAccountProvider[] = []
|
public socialAccountProviders: SocialAccountProvider[] = []
|
||||||
|
|
||||||
constructor(
|
|
||||||
private profileService: ProfileService,
|
|
||||||
public activeModal: NgbActiveModal,
|
|
||||||
private toastService: ToastService,
|
|
||||||
private clipboard: Clipboard
|
|
||||||
) {
|
|
||||||
super()
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.networkActive = true
|
this.networkActive = true
|
||||||
this.profileService
|
this.profileService
|
||||||
@@ -198,7 +195,9 @@ export class ProfileEditDialogComponent
|
|||||||
$localize`Password has been changed, you will be logged out momentarily.`
|
$localize`Password has been changed, you will be logged out momentarily.`
|
||||||
)
|
)
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
window.location.href = `${window.location.origin}/accounts/logout/?next=/accounts/login/?next=/`
|
setLocationHref(
|
||||||
|
`${window.location.origin}/accounts/logout/?next=/accounts/login/?next=/`
|
||||||
|
)
|
||||||
}, 2500)
|
}, 2500)
|
||||||
}
|
}
|
||||||
this.activeModal.close()
|
this.activeModal.close()
|
||||||
|
@@ -10,6 +10,7 @@ import {
|
|||||||
fakeAsync,
|
fakeAsync,
|
||||||
tick,
|
tick,
|
||||||
} from '@angular/core/testing'
|
} from '@angular/core/testing'
|
||||||
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||||
import { By } from '@angular/platform-browser'
|
import { By } from '@angular/platform-browser'
|
||||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
|
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
|
||||||
@@ -33,6 +34,8 @@ describe('ShareLinksDialogComponent', () => {
|
|||||||
imports: [
|
imports: [
|
||||||
ShareLinksDialogComponent,
|
ShareLinksDialogComponent,
|
||||||
NgxBootstrapIconsModule.pick(allIcons),
|
NgxBootstrapIconsModule.pick(allIcons),
|
||||||
|
FormsModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
provideHttpClient(withInterceptorsFromDi()),
|
provideHttpClient(withInterceptorsFromDi()),
|
||||||
@@ -223,16 +226,18 @@ describe('ShareLinksDialogComponent', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should disable archive switch & option if no archive available', () => {
|
it('should disable archive switch & option if no archive available', (done) => {
|
||||||
component.hasArchiveVersion = false
|
component.hasArchiveVersion = false
|
||||||
component.ngOnInit()
|
component.ngOnInit()
|
||||||
fixture.detectChanges()
|
fixture.detectChanges()
|
||||||
expect(component.useArchiveVersion).toBeFalsy()
|
expect(component.useArchiveVersion).toBeFalsy()
|
||||||
expect(
|
setTimeout(() => {
|
||||||
fixture.debugElement.query(By.css("input[type='checkbox']")).attributes[
|
// some stupid change detection issue
|
||||||
'ng-reflect-is-disabled'
|
const inputEl = fixture.debugElement.query(By.css('#versionSwitch'))
|
||||||
]
|
.nativeElement as HTMLInputElement
|
||||||
).toBeTruthy()
|
expect(inputEl.disabled).toBeTruthy()
|
||||||
|
done()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should support close', () => {
|
it('should support close', () => {
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { Clipboard } from '@angular/cdk/clipboard'
|
import { Clipboard } from '@angular/cdk/clipboard'
|
||||||
import { Component, Input, OnInit } from '@angular/core'
|
import { Component, Input, OnInit, inject } from '@angular/core'
|
||||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
||||||
@@ -16,6 +16,11 @@ import { environment } from 'src/environments/environment'
|
|||||||
imports: [FormsModule, ReactiveFormsModule, NgxBootstrapIconsModule],
|
imports: [FormsModule, ReactiveFormsModule, NgxBootstrapIconsModule],
|
||||||
})
|
})
|
||||||
export class ShareLinksDialogComponent implements OnInit {
|
export class ShareLinksDialogComponent implements OnInit {
|
||||||
|
private activeModal = inject(NgbActiveModal)
|
||||||
|
private shareLinkService = inject(ShareLinkService)
|
||||||
|
private toastService = inject(ToastService)
|
||||||
|
private clipboard = inject(Clipboard)
|
||||||
|
|
||||||
EXPIRATION_OPTIONS = [
|
EXPIRATION_OPTIONS = [
|
||||||
{ label: $localize`1 day`, value: 1 },
|
{ label: $localize`1 day`, value: 1 },
|
||||||
{ label: $localize`7 days`, value: 7 },
|
{ label: $localize`7 days`, value: 7 },
|
||||||
@@ -58,13 +63,6 @@ export class ShareLinksDialogComponent implements OnInit {
|
|||||||
|
|
||||||
useArchiveVersion: boolean = true
|
useArchiveVersion: boolean = true
|
||||||
|
|
||||||
constructor(
|
|
||||||
private activeModal: NgbActiveModal,
|
|
||||||
private shareLinkService: ShareLinkService,
|
|
||||||
private toastService: ToastService,
|
|
||||||
private clipboard: Clipboard
|
|
||||||
) {}
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
if (this._documentId !== undefined) this.refresh()
|
if (this._documentId !== undefined) this.refresh()
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { Clipboard, ClipboardModule } from '@angular/cdk/clipboard'
|
import { Clipboard, ClipboardModule } from '@angular/cdk/clipboard'
|
||||||
import { Component, OnInit } from '@angular/core'
|
import { Component, OnInit, inject } from '@angular/core'
|
||||||
import {
|
import {
|
||||||
NgbActiveModal,
|
NgbActiveModal,
|
||||||
NgbModalModule,
|
NgbModalModule,
|
||||||
@@ -35,6 +35,13 @@ import { environment } from 'src/environments/environment'
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class SystemStatusDialogComponent implements OnInit {
|
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)
|
||||||
|
|
||||||
public SystemStatusItemStatus = SystemStatusItemStatus
|
public SystemStatusItemStatus = SystemStatusItemStatus
|
||||||
public PaperlessTaskName = PaperlessTaskName
|
public PaperlessTaskName = PaperlessTaskName
|
||||||
public status: SystemStatus
|
public status: SystemStatus
|
||||||
@@ -49,15 +56,6 @@ export class SystemStatusDialogComponent implements OnInit {
|
|||||||
return this.permissionsService.isSuperUser()
|
return this.permissionsService.isSuperUser()
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
|
||||||
public activeModal: NgbActiveModal,
|
|
||||||
private clipboard: Clipboard,
|
|
||||||
private systemStatusService: SystemStatusService,
|
|
||||||
private tasksService: TasksService,
|
|
||||||
private toastService: ToastService,
|
|
||||||
private permissionsService: PermissionsService
|
|
||||||
) {}
|
|
||||||
|
|
||||||
public ngOnInit() {
|
public ngOnInit() {
|
||||||
this.versionMismatch =
|
this.versionMismatch =
|
||||||
environment.production &&
|
environment.production &&
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { Component, Input } from '@angular/core'
|
import { Component, inject, Input } from '@angular/core'
|
||||||
import { Tag } from 'src/app/data/tag'
|
import { Tag } from 'src/app/data/tag'
|
||||||
import {
|
import {
|
||||||
PermissionAction,
|
PermissionAction,
|
||||||
@@ -13,14 +13,12 @@ import { TagService } from 'src/app/services/rest/tag.service'
|
|||||||
styleUrls: ['./tag.component.scss'],
|
styleUrls: ['./tag.component.scss'],
|
||||||
})
|
})
|
||||||
export class TagComponent {
|
export class TagComponent {
|
||||||
|
private permissionsService = inject(PermissionsService)
|
||||||
|
private tagService = inject(TagService)
|
||||||
|
|
||||||
private _tag: Tag
|
private _tag: Tag
|
||||||
private _tagID: number
|
private _tagID: number
|
||||||
|
|
||||||
constructor(
|
|
||||||
private permissionsService: PermissionsService,
|
|
||||||
private tagService: TagService
|
|
||||||
) {}
|
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
public set tag(tag: Tag) {
|
public set tag(tag: Tag) {
|
||||||
this._tag = tag
|
this._tag = tag
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { Clipboard } from '@angular/cdk/clipboard'
|
import { Clipboard } from '@angular/cdk/clipboard'
|
||||||
import { DecimalPipe } from '@angular/common'
|
import { DecimalPipe } from '@angular/common'
|
||||||
import { Component, EventEmitter, Input, Output } from '@angular/core'
|
import { Component, EventEmitter, Input, Output, inject } from '@angular/core'
|
||||||
import {
|
import {
|
||||||
NgbProgressbarModule,
|
NgbProgressbarModule,
|
||||||
NgbToastModule,
|
NgbToastModule,
|
||||||
@@ -21,6 +21,8 @@ import { Toast } from 'src/app/services/toast.service'
|
|||||||
styleUrl: './toast.component.scss',
|
styleUrl: './toast.component.scss',
|
||||||
})
|
})
|
||||||
export class ToastComponent {
|
export class ToastComponent {
|
||||||
|
private clipboard = inject(Clipboard)
|
||||||
|
|
||||||
@Input() toast: Toast
|
@Input() toast: Toast
|
||||||
|
|
||||||
@Input() autohide: boolean = true
|
@Input() autohide: boolean = true
|
||||||
@@ -31,8 +33,6 @@ export class ToastComponent {
|
|||||||
|
|
||||||
public copied: boolean = false
|
public copied: boolean = false
|
||||||
|
|
||||||
constructor(private clipboard: Clipboard) {}
|
|
||||||
|
|
||||||
onShown(toast: Toast) {
|
onShown(toast: Toast) {
|
||||||
if (!this.autohide) return
|
if (!this.autohide) return
|
||||||
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { Component, OnDestroy, OnInit } from '@angular/core'
|
import { Component, OnDestroy, OnInit, inject } from '@angular/core'
|
||||||
import {
|
import {
|
||||||
NgbAccordionModule,
|
NgbAccordionModule,
|
||||||
NgbProgressbarModule,
|
NgbProgressbarModule,
|
||||||
@@ -20,7 +20,7 @@ import { ToastComponent } from '../toast/toast.component'
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class ToastsComponent implements OnInit, OnDestroy {
|
export class ToastsComponent implements OnInit, OnDestroy {
|
||||||
constructor(public toastService: ToastService) {}
|
toastService = inject(ToastService)
|
||||||
|
|
||||||
private subscription: Subscription
|
private subscription: Subscription
|
||||||
|
|
||||||
|
@@ -5,7 +5,7 @@ import {
|
|||||||
DragDropModule,
|
DragDropModule,
|
||||||
moveItemInArray,
|
moveItemInArray,
|
||||||
} from '@angular/cdk/drag-drop'
|
} from '@angular/cdk/drag-drop'
|
||||||
import { Component } from '@angular/core'
|
import { Component, inject } from '@angular/core'
|
||||||
import { RouterModule } from '@angular/router'
|
import { RouterModule } from '@angular/router'
|
||||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
||||||
import { TourNgBootstrapModule, TourService } from 'ngx-ui-tour-ng-bootstrap'
|
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 {
|
export class DashboardComponent extends ComponentWithPermissions {
|
||||||
|
settingsService = inject(SettingsService)
|
||||||
|
savedViewService = inject(SavedViewService)
|
||||||
|
private tourService = inject(TourService)
|
||||||
|
private toastService = inject(ToastService)
|
||||||
|
|
||||||
public dashboardViews: SavedView[] = []
|
public dashboardViews: SavedView[] = []
|
||||||
constructor(
|
constructor() {
|
||||||
public settingsService: SettingsService,
|
|
||||||
public savedViewService: SavedViewService,
|
|
||||||
private tourService: TourService,
|
|
||||||
private toastService: ToastService
|
|
||||||
) {
|
|
||||||
super()
|
super()
|
||||||
|
|
||||||
this.savedViewService.listAll().subscribe(() => {
|
this.savedViewService.listAll().subscribe(() => {
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user