Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
08821ea8a8 | ||
|
|
e9a466fa31 | ||
|
|
de9f686480 | ||
|
|
d08e9650ba | ||
|
|
0434a96c3a | ||
|
|
2b9b77bef2 | ||
|
|
f5040ad2f4 | ||
|
|
7bc77ae8c0 | ||
|
|
715269a5d0 | ||
|
|
53605c24f0 | ||
|
|
da0b741b69 | ||
|
|
73f923bc47 | ||
|
|
25a8c7ea80 | ||
|
|
950de7c954 | ||
|
|
42ba9240b0 | ||
|
|
3da6299021 | ||
|
|
1ac0f412b9 | ||
|
|
e7fbdc2727 | ||
|
|
7117303d57 | ||
|
|
0ca43dc147 | ||
|
|
c47ea2716c | ||
|
|
cbdcc61414 | ||
|
|
194a7d4cf8 | ||
|
|
9695785a0c | ||
|
|
d9f555554e | ||
|
|
6e38bcf349 | ||
|
|
fc780a90ae | ||
|
|
d586f691b7 | ||
|
|
9e8e43bd12 | ||
|
|
c554a96b4b | ||
|
|
3ecc81094f | ||
|
|
811ccc1baa | ||
|
|
d7c5451729 | ||
|
|
96884372b4 | ||
|
|
dddba4d64a | ||
|
|
a0e3efe2d1 | ||
|
|
d707e1576a | ||
|
|
3aaf363413 | ||
|
|
28fff10fec | ||
|
|
8729c24e1d | ||
|
|
eee895105a | ||
|
|
ef0bf3156e | ||
|
|
a25834b089 | ||
|
|
526bd1f170 | ||
|
|
bea81f83f7 | ||
|
|
03381f47bf | ||
|
|
ec8f962615 | ||
|
|
dd8be14fdb | ||
|
|
12474e5be6 | ||
|
|
34691b8edf | ||
|
|
f98a336920 | ||
|
|
918a05cfda | ||
|
|
84e8478e94 | ||
|
|
fd6692ea1a | ||
|
|
ddc8b416c0 | ||
|
|
a4e98a33c5 | ||
|
|
ee99ee4d02 | ||
|
|
acf9f836a7 | ||
|
|
7c54d1accf | ||
|
|
891db0216b | ||
|
|
f1d9e4f25c | ||
|
|
82ee9f18eb | ||
|
|
ed5feee33a | ||
|
|
3566b889be | ||
|
|
db81701d38 | ||
|
|
cb706416c6 | ||
|
|
f8fb9d7407 | ||
|
|
5cc420b289 | ||
|
|
327905bb90 | ||
|
|
f1481d702c | ||
|
|
c97dbd3cff | ||
|
|
d2602a7629 | ||
|
|
744b40e7f7 | ||
|
|
1e42e54db1 | ||
|
|
8a6689f4fa | ||
|
|
092a65b025 | ||
|
|
5e04abaa06 | ||
|
|
fe8e0ef78b | ||
|
|
ecdb03aca8 | ||
|
|
58e492872a | ||
|
|
ee20e6175a | ||
|
|
24ae38713b | ||
|
|
c8585d1a69 | ||
|
|
3be685ad08 | ||
|
|
aaa2abced4 | ||
|
|
ddc18aa177 | ||
|
|
07130bdc3a | ||
|
|
2f92e407cc | ||
|
|
6e72606d74 | ||
|
|
577f5ae69a | ||
|
|
8ad7e7c905 | ||
|
|
b4e9d9437e | ||
|
|
0c70aedeb1 | ||
|
|
99e90c0b02 | ||
|
|
38744dfd5d | ||
|
|
eb07be1a62 | ||
|
|
825d1add49 | ||
|
|
a15831eb33 | ||
|
|
871e0acd8a | ||
|
|
5af908b2e5 | ||
|
|
449e9a911c | ||
|
|
098c99fd2e | ||
|
|
c9591fe464 | ||
|
|
df6a9b67fd | ||
|
|
d02c349231 | ||
|
|
fd3f1de861 | ||
|
|
f38a5c9220 | ||
|
|
9d2e79f725 | ||
|
|
85889fff56 | ||
|
|
8ac0eb4aa4 | ||
|
|
1966cc3c0e | ||
|
|
20bdb01187 | ||
|
|
3ff56d7987 | ||
|
|
46aa2fff0f | ||
|
|
a10f9ca225 | ||
|
|
0bc1832050 | ||
|
|
78811d916a | ||
|
|
f19f19bb75 | ||
|
|
f2fdbad7d4 | ||
|
|
4b9b9da122 | ||
|
|
10832165c3 | ||
|
|
d6c3b5a9a1 | ||
|
|
2f6338484b | ||
|
|
f586e51aad | ||
|
|
fd12c8705e | ||
|
|
5545b0e447 | ||
|
|
043a6dd049 | ||
|
|
30769fb6ea | ||
|
|
553a97f03d | ||
|
|
8c9623dd0e | ||
|
|
9b232a326a | ||
|
|
68b9d391b0 | ||
|
|
b718aa1fbd | ||
|
|
a114b9c8d3 | ||
|
|
48e045b4a3 | ||
|
|
8439c9cc1e | ||
|
|
4bd02a1181 | ||
|
|
f2d90f31bc | ||
|
|
dcf88560a7 | ||
|
|
2dc541571c | ||
|
|
08c2cd6b63 | ||
|
|
bb25a3397b | ||
|
|
918c2224d8 | ||
|
|
323ecdaab6 | ||
|
|
51040cf1af | ||
|
|
e5e0db1f6a | ||
|
|
62412ce29b | ||
|
|
7c42fb5d75 | ||
|
|
42c2d0884b | ||
|
|
ec0c05fd47 | ||
|
|
8711f7a935 | ||
|
|
7f27cee6a3 | ||
|
|
5644130f01 | ||
|
|
38c30d1eee | ||
|
|
471d0fcd4a | ||
|
|
0b424fe95e | ||
|
|
70615d6843 | ||
|
|
2982acc0ec | ||
|
|
2c6bc7742f | ||
|
|
02a7958865 | ||
|
|
af639a180c | ||
|
|
faa797f461 | ||
|
|
e2eb711ac9 | ||
|
|
7dcb5fec9a | ||
|
|
f507a46324 | ||
|
|
a863b5d375 | ||
|
|
0a81855bc1 | ||
|
|
6064d72aa7 | ||
|
|
35dea13b22 | ||
|
|
7ef6ddf8f7 | ||
|
|
c37fe49890 | ||
|
|
413d1f9ad7 | ||
|
|
3317280062 | ||
|
|
136ef58820 | ||
|
|
1eb1aa2575 | ||
|
|
43e38fae17 | ||
|
|
af54ee6c5c | ||
|
|
161fd1f1b6 | ||
|
|
479b786761 | ||
|
|
daeefeb487 | ||
|
|
a56331d39d | ||
|
|
42c458a736 | ||
|
|
5cf57a520c | ||
|
|
b500981434 | ||
|
|
b0c41ca436 | ||
|
|
df1f805294 | ||
|
|
e640149a23 | ||
|
|
5e36bf797e | ||
|
|
038e4ba386 | ||
|
|
0231d79b5a | ||
|
|
fe0e22a5cd | ||
|
|
20efacfa59 | ||
|
|
5e84a237fb | ||
|
|
f9e0b43707 | ||
|
|
ed0ef385e9 | ||
|
|
f2a0ae108c | ||
|
|
a721d9286b | ||
|
|
bc2baa3bbd | ||
|
|
51660f7677 | ||
|
|
f08039b23c | ||
|
|
9d0dcda60f | ||
|
|
c42fbd6e0e | ||
|
|
0b14c311b4 | ||
|
|
31d427546b |
+12
-12
@@ -364,7 +364,7 @@ steps:
|
||||
- api-build
|
||||
|
||||
- name: frontend-dependencies
|
||||
image: node:20.14.0-alpine
|
||||
image: node:20.16.0-alpine
|
||||
pull: always
|
||||
environment:
|
||||
PNPM_CACHE_FOLDER: .cache/pnpm
|
||||
@@ -378,7 +378,7 @@ steps:
|
||||
# - restore-cache
|
||||
|
||||
- name: frontend-lint
|
||||
image: node:20.14.0-alpine
|
||||
image: node:20.16.0-alpine
|
||||
pull: always
|
||||
environment:
|
||||
PNPM_CACHE_FOLDER: .cache/pnpm
|
||||
@@ -390,7 +390,7 @@ steps:
|
||||
- frontend-dependencies
|
||||
|
||||
- name: frontend-build-prod
|
||||
image: node:20.14.0-alpine
|
||||
image: node:20.16.0-alpine
|
||||
pull: always
|
||||
environment:
|
||||
PNPM_CACHE_FOLDER: .cache/pnpm
|
||||
@@ -402,7 +402,7 @@ steps:
|
||||
- frontend-dependencies
|
||||
|
||||
- name: frontend-test-unit
|
||||
image: node:20.14.0-alpine
|
||||
image: node:20.16.0-alpine
|
||||
pull: always
|
||||
commands:
|
||||
- cd frontend
|
||||
@@ -413,7 +413,7 @@ steps:
|
||||
|
||||
- name: frontend-typecheck
|
||||
failure: ignore
|
||||
image: node:20.14.0-alpine
|
||||
image: node:20.16.0-alpine
|
||||
pull: always
|
||||
environment:
|
||||
PNPM_CACHE_FOLDER: .cache/pnpm
|
||||
@@ -544,7 +544,7 @@ steps:
|
||||
- git fetch --tags
|
||||
|
||||
- name: frontend-dependencies
|
||||
image: node:20.14.0-alpine
|
||||
image: node:20.16.0-alpine
|
||||
pull: always
|
||||
environment:
|
||||
PNPM_CACHE_FOLDER: .cache/pnpm
|
||||
@@ -556,7 +556,7 @@ steps:
|
||||
- pnpm install --fetch-timeout 100000
|
||||
|
||||
- name: frontend-build
|
||||
image: node:20.14.0-alpine
|
||||
image: node:20.16.0-alpine
|
||||
pull: always
|
||||
environment:
|
||||
PNPM_CACHE_FOLDER: .cache/pnpm
|
||||
@@ -716,7 +716,7 @@ steps:
|
||||
|
||||
# Build os packages and push it to our bucket
|
||||
- name: build-os-packages-unstable
|
||||
image: goreleaser/nfpm:v2.37.1
|
||||
image: goreleaser/nfpm:v2.38.0
|
||||
pull: always
|
||||
commands:
|
||||
- apk add git go
|
||||
@@ -732,7 +732,7 @@ steps:
|
||||
depends_on: [ after-build-compress ]
|
||||
|
||||
- name: build-os-packages-version
|
||||
image: goreleaser/nfpm:v2.37.1
|
||||
image: goreleaser/nfpm:v2.38.0
|
||||
pull: always
|
||||
commands:
|
||||
- apk add git go
|
||||
@@ -901,7 +901,7 @@ steps:
|
||||
- git fetch --tags
|
||||
|
||||
- name: build
|
||||
image: node:20.14.0-alpine
|
||||
image: node:20.16.0-alpine
|
||||
pull: always
|
||||
environment:
|
||||
PNPM_CACHE_FOLDER: .cache/pnpm
|
||||
@@ -962,7 +962,7 @@ steps:
|
||||
- git fetch --tags
|
||||
|
||||
- name: build
|
||||
image: node:20.14.0-alpine
|
||||
image: node:20.16.0-alpine
|
||||
pull: always
|
||||
environment:
|
||||
PNPM_CACHE_FOLDER: .cache/pnpm
|
||||
@@ -1352,6 +1352,6 @@ steps:
|
||||
- failure
|
||||
---
|
||||
kind: signature
|
||||
hmac: 3cc4b45fcbbab5ccee14e5532b2d35f5db54cde29a8f6e854b563462e3bbb509
|
||||
hmac: dc96a5f8aaa332b35fc02f1b8566493e494452d62bed52973d804f5347ae47ae
|
||||
|
||||
...
|
||||
|
||||
@@ -1 +1,3 @@
|
||||
use flake
|
||||
source_url "https://raw.githubusercontent.com/cachix/devenv/95f329d49a8a5289d31e0982652f7058a189bfca/direnvrc" "sha256-d+8cBpDfDBj41inrADaJt+bDWhOktwslgoP5YiGJ1v0="
|
||||
|
||||
use devenv
|
||||
+10
-1
@@ -28,4 +28,13 @@ vendor/
|
||||
os-packages/
|
||||
mage_output_file.go
|
||||
mage-static
|
||||
.direnv/
|
||||
|
||||
# Devenv
|
||||
.devenv*
|
||||
devenv.local.nix
|
||||
|
||||
# direnv
|
||||
.direnv
|
||||
|
||||
# pre-commit
|
||||
.pre-commit-config.yaml
|
||||
|
||||
+3
-2
@@ -4,7 +4,9 @@ run:
|
||||
|
||||
linters:
|
||||
enable:
|
||||
- megacheck
|
||||
- gosimple
|
||||
- staticcheck
|
||||
- unused
|
||||
- govet
|
||||
- gocritic
|
||||
- gocyclo
|
||||
@@ -15,7 +17,6 @@ linters:
|
||||
- revive
|
||||
- misspell
|
||||
disable:
|
||||
- scopelint # Obsolete, using exportloopref instead
|
||||
- durationcheck
|
||||
- goconst
|
||||
- musttag
|
||||
|
||||
Vendored
+1
-1
@@ -26,7 +26,7 @@
|
||||
|
||||
// i18n ally
|
||||
"i18n-ally.localesPaths": [
|
||||
"src/i18n/lang"
|
||||
"frontend/src/i18n/lang"
|
||||
],
|
||||
"i18n-ally.sortKeys": true,
|
||||
"i18n-ally.keepFulfilled": true,
|
||||
|
||||
+213
-1
@@ -5,7 +5,219 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres
|
||||
to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
All releases can be found on https://code.vikunja.io/api/releases.
|
||||
All releases can be found on https://code.vikunja.io/vikunja/releases.
|
||||
|
||||
## [0.24.2] - 2024-08-12
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* *(i18n)* Change casing of Ukrainian language in selector
|
||||
* *(kanban)* Always make cover image full width
|
||||
* *(mail)* Do not fail testmail command when the connection could not be closed.
|
||||
* *(migration)* Make sure tasks are associated to the correct view and bucket for data imported from Vikunja dump
|
||||
* *(migration)* Ensure project background gets exported and imported
|
||||
* *(projects)* Trigger only single mutation
|
||||
* *(task)* Do not allow moving a task to the project the task already belongs to
|
||||
* *(task)* Set current project after moving a task
|
||||
* *(task)* Move task into new kanban bucket when moving between projects
|
||||
* *(views)* Do not create task bucket and task position entries when duplicating a project* Emit for DatepickerWithValues ([3aaf363](3aaf3634134a6989337bad02ac99a9329d33b17f))
|
||||
* Textarea autosize for LanguageTool ([d9f5555](d9f555554e5ecfa9d1243c565e2f42c77f7a7597))
|
||||
* Remove console log ([0ca43dc](0ca43dc147acd04d9f9b566325ccde0a5782680f))
|
||||
|
||||
### Dependencies
|
||||
|
||||
* *(deps)* Update module github.com/coreos/go-oidc/v3 to v3.11.0
|
||||
* *(deps)* Update flake
|
||||
* *(deps)* Update go toolchain to 1.22.5
|
||||
* *(deps)* Update dependency node to v20.16.0
|
||||
|
||||
### Documentation
|
||||
|
||||
* Clarify Todoist redirect url ([7117303](7117303d5705199bb39fb6661b20deebea96959f))
|
||||
|
||||
### Features
|
||||
|
||||
* *(editor)* Support custom protocol for links* Use withDefaults for Reminders ([8729c24](8729c24e1d2bdc783e750dc1166a8ab31a716107))
|
||||
* Improve projects store ([d707e15](d707e1576a006baa73c529b432e694dee237db76))
|
||||
* Improve label store ([a0e3efe](a0e3efe2d12521e773334a19ac985a2011575c3c))
|
||||
* Improve priority visibility ([dddba4d](dddba4d64a9ddddd6f442c8180661d2d498de994))
|
||||
* Add tailwind with prefix (#2513) ([d7c5451](d7c54517297e750d92618376f52045b11da6e82c))
|
||||
* Improve ProjectSettingsViews ([811ccc1](811ccc1baa2fe1fb9fcc8b5515c8c3f3b591e96a))
|
||||
* Add missing peer dependency ([d586f69](d586f691b7a245bbffd5df4e7a4937f0527047e0))
|
||||
* Switch from nix flakes to devenv ([73f923b](73f923bc47d32a6a9a689e461901295b07646ebf))
|
||||
|
||||
|
||||
### Miscellaneous Tasks
|
||||
|
||||
* *(i18n)* Update translations via Crowdin
|
||||
* Remove lodash.debounce ([fc780a9](fc780a90ae856038db73d3ad63fcdd5627211c36))
|
||||
* Improve error message ([6e38bcf](6e38bcf3498a15a1dc1f6fbef254c08513b408b3))
|
||||
* Use nixpkgs unstable for more recent packages ([f5040ad](f5040ad2f4996fe1ccf03172b1a1ee6007068ee7))
|
||||
|
||||
### Other
|
||||
|
||||
* *(other)* [skip ci] Updated swagger docs
|
||||
|
||||
## [0.24.1] - 2024-07-18
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* *(api tokens)* Show error message when the user tries to create an api token without at least one permission selected
|
||||
* *(filter)* Make sure filter values are properly escaped before executing them
|
||||
* *(filters)* Add task to buckets of saved filters when creating the task
|
||||
* *(filters)* Add tasks to filter buckets when updating the filter
|
||||
* *(filters)* Do not create a default filter for list view when creating a saved filter
|
||||
* *(filters)* Immediately emit filter query when editing saved filter
|
||||
* *(filters)* Make sure filters are replaced case-insensitively before filtering on the server
|
||||
* *(filters)* Only insert task buckets and positions when there are any
|
||||
* *(filters)* Reload tasks silently when marking one done in the list
|
||||
* *(filters)* Show actual error message from api when the filter query is invalid
|
||||
* *(filters)* Trim spaces when parsing filter values
|
||||
* *(kanban)* Dispatch task updated event when task is moved between buckets
|
||||
* *(kanban)* Dispatch task updated event when task position is updated
|
||||
* *(kanban)* Do not allow to create new tasks in saved filter
|
||||
* *(kanban)* Do not move repeating task into a different bucket
|
||||
* *(kanban)* Make sure tasks which changed their done status are moved around in buckets
|
||||
* *(kanban)* Move repeating task back to old bucket when moved to the done bucket
|
||||
* *(kanban)* Move task to done bucket in all views when moved to done bucket in one view
|
||||
* *(kanban)* Move task to done bucket when it was marked done from the task detail view
|
||||
* *(kanban)* Put task into correct bucket when creating via kanban board
|
||||
* *(kanban)* Update task done status after moving it into done bucket
|
||||
* *(kanban)* Use correct assertion in the test
|
||||
* *(kanban)* Use correct text color for deletion button
|
||||
* *(migration)* Correctly set bucket for related tasks
|
||||
* *(migration)* Failed migration typo
|
||||
* *(migration)* Revert to old path for migration routing
|
||||
* *(project)* Do not use project id of nil project in error
|
||||
* *(projects)* Do not create backlog bucket when duplicating views
|
||||
* *(projects)* Do not create buckets in the original project when duplicating a project
|
||||
* *(quick add magic)* Create the task even when it only contains quick add magic keywords
|
||||
* *(settings)* Overflow of select on mobile
|
||||
* *(task)* Use backdropView prop
|
||||
* *(tasks)* Do not use typesense modified options to search with database
|
||||
* *(tasks)* Explicitly add task position to select statement when looking up tasks with Typesense
|
||||
* *(tasks)* Limit to max 250 entries when using typesense
|
||||
* *(translation)* TOTP casing
|
||||
* *(typesense)* Do not crash after creating a project when tasks are not yet indexed
|
||||
* *(typesense)* Do not use modified opts for db fallback search
|
||||
* *(typesense)* Reindex tasks when their position changed
|
||||
* *(vscode)* I18n-ally locales path* ProjectSearch default value ([f08039b](f08039b23c1ff8ca208f5c5911f788780ecc80ee))
|
||||
* Add info log message when starting to run migrations ([5e36bf7](5e36bf797e99d99f83cb084f8c1707d994d0559f))
|
||||
* Add missing disabled prop ([ed0ef38](ed0ef385e9ba34524d3309bec87eeb44d9dba471))
|
||||
* App bottom padding ([51660f7](51660f76779e189bca2a24f1e4fca34b8a1a2898))
|
||||
* Disable button if loading ([a721d92](a721d9286bbb26e0a182992af6b2128e03d5670f))
|
||||
* Dropdown item disabled prop ([3317280](3317280062c8506d092d5121b92eb4992177f9c2))
|
||||
* Gitignore dist path ([7ef6ddf](7ef6ddf8f7630e6370bed762779b4ebabbf8962b))
|
||||
* Lint ([7c42fb5](7c42fb5d75fd9a5aa01abaa852452875f07a1f61))
|
||||
* Missing error handling ([744b40e](744b40e7f780851d5ce8a288271f78acc462ed14))
|
||||
* Muliselect optional props ([0a81855](0a81855bc1b403233dec18f036c80f3cad70edee))
|
||||
* Reorder mail options (#2533) ([136ef58](136ef58820b8e2d27ad0ca50bdd9adabd8e4a95d))
|
||||
* Scss deprecation warning ([db81701](db81701d3841e12c39b751e919d70fdafc1869d6))
|
||||
* Spelling mail ([2dc5415](2dc541571cbecc02a178182217d1e4596e4a62aa))
|
||||
* Wrapped button ([af639a1](af639a180cf56519e9a3a31710da6fee5b305735))
|
||||
|
||||
### Dependencies
|
||||
|
||||
* *(deps)* Update dependency @github/hotkey to v3.1.1 (#2329)
|
||||
* *(deps)* Update dependency @sentry/vue to v8.14.0
|
||||
* *(deps)* Update dependency @sentry/vue to v8.15.0
|
||||
* *(deps)* Update dependency @sentry/vue to v8.17.0
|
||||
* *(deps)* Update dependency @sentry/vue to v8.18.0
|
||||
* *(deps)* Update dependency dayjs to v1.11.12
|
||||
* *(deps)* Update dependency dompurify to v3.1.6
|
||||
* *(deps)* Update dependency ufo to v1.5.4
|
||||
* *(deps)* Update dependency vue to v3.4.32
|
||||
* *(deps)* Update dependency vue-tsc to v2.0.26
|
||||
* *(deps)* Update dev-dependencies
|
||||
* *(deps)* Update dev-dependencies
|
||||
* *(deps)* Update dev-dependencies
|
||||
* *(deps)* Update dev-dependencies
|
||||
* *(deps)* Update flake
|
||||
* *(deps)* Update font awesome to v6.6.0
|
||||
* *(deps)* Update goreleaser/nfpm docker tag to v2.38.0
|
||||
* *(deps)* Update module github.com/arran4/golang-ical to v0.3.1 (#2606)
|
||||
* *(deps)* Update module github.com/microcosm-cc/bluemonday to v1.0.27
|
||||
* *(deps)* Update module golang.org/x/crypto to v0.25.0
|
||||
* *(deps)* Update module golang.org/x/term to v0.22.0
|
||||
* *(deps)* Update pnpm to v9.5.0
|
||||
* *(deps)* Update tiptap to v2.5.4
|
||||
|
||||
### Features
|
||||
|
||||
* *(Multiselect)* Hide multiselect during loading
|
||||
* *(project)* Add " - duplicate" suffix to duplicated projects title* Move powered by to bottom ([f9e0b43](f9e0b4370700b4cc9c8e99923737bb5aa022a1d0))
|
||||
* Add withDefaults and emit types to PrioritySelect ([aaa2abc](aaa2abced4258645a9a47b228cece21741bfea25))
|
||||
* Add withDefaults, defineEmits and defineSlots types for Dropdown ([5545b0e](5545b0e447d406837324186c765cf2e8c8ed47c4))
|
||||
* Define prop and emit types DatepickerWithRange ([daeefeb](daeefeb487966bfc6535b3d5c8e1dd07ec8a0951))
|
||||
* Define prop and emit types for FilterPopup ([9d2e79f](9d2e79f7253006d504bd916ab8d820187d50700b))
|
||||
* DefineModel and withDefaults for PercentDoneSelect ([8ac0eb4](8ac0eb4aa4fbe5a3b147e7f31a29aa2c2f84a1ca))
|
||||
* Improve BaseButtonEmits type ([c37fe49](c37fe4989015ee18b38bd89b9470c14484759cc8))
|
||||
* Improve main nav spacing and open speed ([faa797f](faa797f461a57e8a9e115b760af92ab0024f6eed))
|
||||
* Improve settings label casing ([20efacf](20efacfa59a7f7ff81fd17c2c3b3c9a1d5528e5d))
|
||||
* Remove PropTypes helper from FilePreview ([0bc1832](0bc18320509e6db7ae484f7336282b7e8e5e841e))
|
||||
* Remove PropTypes helper from ProjectInfo ([8ad7e7c](8ad7e7c9055e50768ea5b0ad5798d395bb5d3514))
|
||||
* Remove PropTypes helper from ProjectSettingsEdit ([85889ff](85889fff5603f0b7c7a0f833cae7c32610eeede4))
|
||||
* Remove eslint rule vue/no-required-prop-with-default ([df1f805](df1f805294e34f8bfaaf60ea65f18cabbd932d9c))
|
||||
* Remove props destructuring ProjectSettingsViews ([20bdb01](20bdb011877fe17fe5c266eafe77b96dce1646d9))
|
||||
* Remove props destructuring for DeferTask ([46aa2ff](46aa2fff0f01bf233e4edd39e6675c358c488794))
|
||||
* Remove props destructuring for Filters ([3ff56d7](3ff56d79874d013ad97c8635fd580edf95290b39))
|
||||
* Remove props destructuring for ProjectView ([553a97f](553a97f03de3e16593c006ca4a9043fadf5d6a6f))
|
||||
* Remove props destructuring for ProjectWrapper ([38744df](38744dfd5db0acb53ae03db5e2021265876df2f7))
|
||||
* Remove props destructuring from Attachments ([07130bd](07130bdc3a65bfb4ed1775e61312b68c62521a4a))
|
||||
* Remove props destructuring from ColorPicker ([eb07be1](eb07be1a62a9af377b044a7e0268d9d7a8bab77c))
|
||||
* Remove props destructuring from FilterDelete ([825d1ad](825d1add49595184593ff6fb3ab228cf3fbde9cb))
|
||||
* Remove props destructuring from FilterEdit ([449e9a9](449e9a911c2fcca7712479cd78dbfe80d5a4bde2))
|
||||
* Remove props destructuring from FilterInput ([fd6692e](fd6692ea1afce3950c2295a4e8fb551af80e52aa))
|
||||
* Remove props destructuring from KanbanCard ([ddc18aa](ddc18aa17739963afa5d156241aa07b7100374f4))
|
||||
* Remove props destructuring from NoAuthWrapper ([c9591fe](c9591fe464e24f38126d5562ef7d0e6060f14b39))
|
||||
* Remove props destructuring from ProjectKanban ([5af908b](5af908b2e5508712004ee1c702915e4def0ca007))
|
||||
* Remove props destructuring from ProjectList ([0c70aed](0c70aedeb1a5ca05db3cc2b87138d28427eac959))
|
||||
* Remove props destructuring from ProjectTable ([99e90c0](99e90c0b02561f1c7b8f49b93ece11b878137e48))
|
||||
* Remove props destructuring from ShowTasks ([d02c349](d02c349231f29326511755dcae5003d3b3c01ca0))
|
||||
* Remove props destructuring from SingleTaskInProject ([a15831e](a15831eb33b5104e19f0b85b882dd8ec5336ba62))
|
||||
* Remove props destructuring from TaskDetailView ([a10f9ca](a10f9ca22580f6fcc435f152776867b380d5be6c))
|
||||
* Remove props destructuring from ViewEditForm ([2f92e40](2f92e407ccf42892675dc1583dfa057474cb2489))
|
||||
* Remove unnecessary prop from Card ([1eb1aa2](1eb1aa257574733894f865913a4bb173e22774ea))
|
||||
* Rename TheNavigation to AppHeader ([43e38fa](43e38fae17d987d96820dcdfd936fcf8d11899ff))
|
||||
* Simplify playPopSound setting check ([42c458a](42c458a736d88bf4b0f18fa23c08273ddf9c7ca5))
|
||||
* Type defineEmits for ApiConfig ([1966cc3](1966cc3c0e7a590651db2a5bd0acdcd83e948aa7))
|
||||
* Type defineEmits for Card ([043a6dd](043a6dd049ba22fa1c87ed88aa0b2f8dba4f2593))
|
||||
* Type defineEmits for CreateEdit ([098c99f](098c99fd2ede44ddf231e1b504b9cf6d8c0ed17b))
|
||||
* Type defineEmits for Heading ([fd3f1de](fd3f1de861ac593c0f780c3f341a4b0c5d7b2f1b))
|
||||
* Type defineEmits for ProjectSearch ([f38a5c9](f38a5c9220c6404152e261a78e4829e52cef5d1b))
|
||||
* Use withDefaults for Comments ([f19f19b](f19f19bb75a457dc2073d8a06f064eae04a3b772))
|
||||
* Use withDefaults for DateTableCell ([f586e51](f586e51aad5194043fec37e3b0af94a30c95f3a3))
|
||||
* Use withDefaults for Datepicker ([78811d9](78811d916aeaa105560c5b7c091290f3b2e38683))
|
||||
* Use withDefaults for DatepickerInline ([df6a9b6](df6a9b67fd2a462e5027538e521da174ecd24bc6))
|
||||
* Use withDefaults for DatepickerWithValues ([cb70641](cb706416c61ba31d1fd54d8b9bd8549cc9a33528))
|
||||
* Use withDefaults for EditLabels ([6e72606](6e72606d7454fbb3b17302d3ef1d2cdf186873ae))
|
||||
* Use withDefaults for FancyCheckbox ([b4e9d94](b4e9d9437e4fb4b8e5e56c633fc4c74ee45a6225))
|
||||
* Use withDefaults for Heading ([5cf57a5](5cf57a520cd7048aba0656ce00333cc7cfcc14f9))
|
||||
* Use withDefaults for Pagination ([1083216](10832165c3798bef63590c292426feb67e178f15))
|
||||
* Use withDefaults for Password ([577f5ae](577f5ae69a31e14cdff6e0aed5f3091d07484faa))
|
||||
* Use withDefaults for PriorityLabel ([871e0ac](871e0acd8aa8ba6a9ad7fb08bda8badf20104f94))
|
||||
* Use withDefaults for ProgressBar ([2f63384](2f6338484bda4670133f3db6c122dbaa6a231c15))
|
||||
* Use withDefaults for ProjectCardGrid ([479b786](479b786761948b0a0427676c42e082e75579029c))
|
||||
* Use withDefaults for Reactions ([d6c3b5a](d6c3b5a9a10a04c8fac47ad998ebf434e93dfa71))
|
||||
* Use withDefaults for ReminderPeriod ([4b9b9da](4b9b9da122a2d085dcff8fbb8dd2693062689fd4))
|
||||
* Use withDefaults for RepeatAfter ([c8585d1](c8585d1a691403e6331cd9aff852873c0f7771d3))
|
||||
* Use withDefaults for SelectProject ([fd12c87](fd12c8705ea9b28db6947fd98f2d34ce46fc15b2))
|
||||
* Use withDefaults for SelectUser ([b500981](b50098143474be8d86e0ae0543bab1787585dfe7))
|
||||
* Use withDefaults for Subscription ([30769fb](30769fb6eadb21d45629dcf2c67cc1174d26e828))
|
||||
* Use withDefaults for UserTeam ([f2fdbad](f2fdbad7d4bde5880694a611a4f9d68fe2017367))
|
||||
* Use withDefaults in ReminderDetail ([a56331d](a56331d39d12c65838b13987fe2096c4ca2a703f))
|
||||
* WithDefaults for EditAssignees ([f1481d7](f1481d702cc426cddfeda0edae6a6cd06d9f3380))
|
||||
* WithDefaults for Multiselect ([413d1f9](413d1f9ad7e9ba0234d81a1564745cd03d9f1044))
|
||||
|
||||
### Miscellaneous Tasks
|
||||
|
||||
* *(i18n)* Update translations via Crowdin
|
||||
* *(popup)* Trigger close function directly
|
||||
* *(project)* Rename receiver
|
||||
* 0.24.0 release preparations ([0b14c31](0b14c311b4d344aecf974489eee19ab939c676ad))
|
||||
* Go mod tidy ([e640149](e640149a23c931838a93b737804287a9ed570268))
|
||||
* Update golangci lint config ([d2602a7](d2602a7629ee3d8cd67beb8ef1a44ca3cd2dd7f9))
|
||||
* *(other)* [skip ci] Updated swagger docs
|
||||
|
||||
## [0.24.0] - 2024-07-02
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
FROM --platform=$BUILDPLATFORM node:20.14.0-alpine AS frontendbuilder
|
||||
FROM --platform=$BUILDPLATFORM node:20.16.0-alpine AS frontendbuilder
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
[](https://drone.kolaente.de/vikunja/vikunja)
|
||||
[](LICENSE)
|
||||
[](https://dl.vikunja.io)
|
||||
[](https://vikunja.io/docs/installing)
|
||||
[](https://hub.docker.com/r/vikunja/vikunja/)
|
||||
[](https://try.vikunja.io/api/v1/docs)
|
||||
[](https://goreportcard.com/report/kolaente.dev/vikunja/vikunja)
|
||||
|
||||
+6
-5
@@ -227,12 +227,13 @@ migration:
|
||||
clientid:
|
||||
# The client secret, also required for making requests to the todoist api
|
||||
clientsecret:
|
||||
# The url where clients are redirected after they authorized Vikunja to access their todoist items.
|
||||
# This needs to match the url you entered when registering your Vikunja instance at todoist.
|
||||
# This is usually the frontend url where the frontend then makes a request to /migration/todoist/migrate
|
||||
# The url where clients are redirected after they authorized Vikunja to access their Todoist items.
|
||||
# In Todoist, this is called `OAuth redirect URL` and it needs to match the url you entered when registering
|
||||
# your Vikunja instance at the Todoist developer console.
|
||||
# When using the official Vikunja frontend, set this to <service.publicurl>/migrate/todoist.
|
||||
# Otherwise, set this to an url which then makes a request to /api/v1/migration/todoist/migrate
|
||||
# with the code obtained from the todoist api.
|
||||
# Note that the Vikunja frontend expects this to be /migrate/todoist
|
||||
redirecturl: <frontend url>/migrate/todoist
|
||||
redirecturl: <service.publicurl>/migrate/todoist
|
||||
trello:
|
||||
# Whether to enable the trello migrator or not
|
||||
enable: false
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"electron": "29.4.3",
|
||||
"electron": "29.4.5",
|
||||
"electron-builder": "24.13.3"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
Generated
+4
-4
@@ -14,8 +14,8 @@ dependencies:
|
||||
|
||||
devDependencies:
|
||||
electron:
|
||||
specifier: 29.4.3
|
||||
version: 29.4.3
|
||||
specifier: 29.4.5
|
||||
version: 29.4.5
|
||||
electron-builder:
|
||||
specifier: 24.13.3
|
||||
version: 24.13.3(electron-builder-squirrel-windows@24.13.3)
|
||||
@@ -917,8 +917,8 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/electron@29.4.3:
|
||||
resolution: {integrity: sha512-Fr/D7Iru7zGFK5DtNf3Ck/DBxLHtP/o/32pRXsSoGTVqyHKu2genIiSUiOhbxdu+gQZw4Do+BDDR6inkyr4CGQ==}
|
||||
/electron@29.4.5:
|
||||
resolution: {integrity: sha512-DlEuzGbWBYl1Qr0qUYgNZdoixJg4YGHy2HC6fkRjSXSlb01UrQ5ORi8hNLzelzyYx8rNQyyE3zDUuk9EnZwYuA==}
|
||||
engines: {node: '>= 12.20.55'}
|
||||
hasBin: true
|
||||
requiresBuild: true
|
||||
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
{
|
||||
"nodes": {
|
||||
"devenv": {
|
||||
"locked": {
|
||||
"dir": "src/modules",
|
||||
"lastModified": 1723409744,
|
||||
"owner": "cachix",
|
||||
"repo": "devenv",
|
||||
"rev": "116159a4bfeaa79662f4c39dbfaf2fce6a5737f7",
|
||||
"treeHash": "ccbae63055f83e669996a88a3cbb4ef69febd002",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"dir": "src/modules",
|
||||
"owner": "cachix",
|
||||
"repo": "devenv",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1696426674,
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
|
||||
"treeHash": "2addb7b71a20a25ea74feeaf5c2f6a6b30898ecb",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"gitignore": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"pre-commit-hooks",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1709087332,
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
|
||||
"treeHash": "ca14199cabdfe1a06a7b1654c76ed49100a689f9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1716977621,
|
||||
"owner": "cachix",
|
||||
"repo": "devenv-nixpkgs",
|
||||
"rev": "4267e705586473d3e5c8d50299e71503f16a6fb6",
|
||||
"treeHash": "6d9f1f7ca0faf1bc2eeb397c78a49623260d3412",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"ref": "rolling",
|
||||
"repo": "devenv-nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-stable": {
|
||||
"locked": {
|
||||
"lastModified": 1723282977,
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "a781ff33ae258bbcfd4ed6e673860c3e923bf2cc",
|
||||
"treeHash": "75cf71293251c603e56d6076098346b4128b2ec8",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-24.05",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-unstable": {
|
||||
"locked": {
|
||||
"lastModified": 1723175592,
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "5e0ca22929f3342b19569b21b2f3462f053e497b",
|
||||
"treeHash": "a6b38d5232ae0ae0c666c37f5ec9620c14f30369",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"pre-commit-hooks": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"gitignore": "gitignore",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"nixpkgs-stable": "nixpkgs-stable"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1723202784,
|
||||
"owner": "cachix",
|
||||
"repo": "pre-commit-hooks.nix",
|
||||
"rev": "c7012d0c18567c889b948781bc74a501e92275d1",
|
||||
"treeHash": "0016d5d748d61d3bfaa69d3c02abff74740a46b7",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"repo": "pre-commit-hooks.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"devenv": "devenv",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"nixpkgs-unstable": "nixpkgs-unstable",
|
||||
"pre-commit-hooks": "pre-commit-hooks"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
{ pkgs, lib, config, inputs, ... }:
|
||||
|
||||
let
|
||||
pkgs-unstable = import inputs.nixpkgs-unstable { system = pkgs.stdenv.system; };
|
||||
in {
|
||||
packages = with pkgs-unstable; [
|
||||
# General tools
|
||||
git-cliff
|
||||
# Frontend tools
|
||||
cypress
|
||||
# API tools
|
||||
golangci-lint mage
|
||||
# Desktop
|
||||
electron
|
||||
];
|
||||
|
||||
languages = {
|
||||
javascript = {
|
||||
enable = true;
|
||||
package = pkgs-unstable.nodejs-slim;
|
||||
pnpm = {
|
||||
enable = true;
|
||||
package = pkgs-unstable.pnpm;
|
||||
};
|
||||
};
|
||||
|
||||
go = {
|
||||
enable = true;
|
||||
};
|
||||
};
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
# yaml-language-server: $schema=https://devenv.sh/devenv.schema.json
|
||||
inputs:
|
||||
nixpkgs:
|
||||
url: github:cachix/devenv-nixpkgs/rolling
|
||||
nixpkgs-unstable:
|
||||
url: github:NixOS/nixpkgs/nixos-unstable
|
||||
|
||||
# If you're using non-OSS software, you can set allowUnfree to true.
|
||||
allowUnfree: true
|
||||
|
||||
# If you're willing to use a package that's vulnerable
|
||||
# permittedInsecurePackages:
|
||||
# - "openssl-1.1.1w"
|
||||
|
||||
# If you have more than one devenv you can merge them
|
||||
#imports:
|
||||
# - ./backend
|
||||
Generated
-25
@@ -1,25 +0,0 @@
|
||||
{
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1718149104,
|
||||
"narHash": "sha256-Ds1QpobBX2yoUDx9ZruqVGJ/uQPgcXoYuobBguyKEh8=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "e913ae340076bbb73d9f4d3d065c2bca7caafb16",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
{
|
||||
description = "Vikunja dev environment";
|
||||
|
||||
outputs = { self, nixpkgs }:
|
||||
let pkgs = nixpkgs.legacyPackages.x86_64-linux;
|
||||
in {
|
||||
defaultPackage.x86_64-linux =
|
||||
pkgs.mkShell { buildInputs = with pkgs; [
|
||||
# General tools
|
||||
git-cliff
|
||||
# Frontend tools
|
||||
pnpm cypress
|
||||
# API tools
|
||||
go golangci-lint mage
|
||||
# Desktop
|
||||
electron
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -42,7 +42,6 @@ module.exports = {
|
||||
'error',
|
||||
{ 'script': { 'lang': 'ts' } },
|
||||
],
|
||||
'vue/no-required-prop-with-default': ['error', { 'autofix': true }],
|
||||
'vue/no-duplicate-attr-inheritance': 'error',
|
||||
'vue/no-empty-component-block': 'error',
|
||||
'vue/html-indent': ['error', 'tab'],
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ stats.html
|
||||
|
||||
node_modules
|
||||
.DS_Store
|
||||
/dist*
|
||||
dist
|
||||
coverage
|
||||
*.zip
|
||||
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
20.15.0
|
||||
20.16.0
|
||||
@@ -116,9 +116,9 @@ describe('Project View Kanban', () => {
|
||||
cy.get('.kanban .bucket .bucket-header .dropdown.options .dropdown-menu .dropdown-item')
|
||||
.contains('Limit: Not Set')
|
||||
.click()
|
||||
cy.get('.kanban .bucket .bucket-header .dropdown.options .dropdown-menu .dropdown-item .field input.input')
|
||||
cy.get('.kanban .bucket .bucket-header .dropdown.options .dropdown-menu .field input.input')
|
||||
.first()
|
||||
.type(3)
|
||||
.type('3')
|
||||
cy.get('[data-cy="setBucketLimit"]')
|
||||
.first()
|
||||
.click()
|
||||
|
||||
+58
-58
@@ -13,7 +13,7 @@
|
||||
},
|
||||
"homepage": "https://vikunja.io/",
|
||||
"funding": "https://opencollective.com/vikunja",
|
||||
"packageManager": "pnpm@9.4.0",
|
||||
"packageManager": "pnpm@9.6.0",
|
||||
"keywords": [
|
||||
"todo",
|
||||
"productivity",
|
||||
@@ -50,49 +50,50 @@
|
||||
"story:preview": "histoire preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-svg-core": "6.5.2",
|
||||
"@fortawesome/free-regular-svg-icons": "6.5.2",
|
||||
"@fortawesome/free-solid-svg-icons": "6.5.2",
|
||||
"@fortawesome/fontawesome-svg-core": "6.6.0",
|
||||
"@fortawesome/free-regular-svg-icons": "6.6.0",
|
||||
"@fortawesome/free-solid-svg-icons": "6.6.0",
|
||||
"@fortawesome/vue-fontawesome": "3.0.8",
|
||||
"@github/hotkey": "3.1.0",
|
||||
"@github/hotkey": "3.1.1",
|
||||
"@infectoone/vue-ganttastic": "2.3.2",
|
||||
"@intlify/unplugin-vue-i18n": "4.0.0",
|
||||
"@kyvg/vue3-notification": "3.2.1",
|
||||
"@sentry/tracing": "7.114.0",
|
||||
"@sentry/vue": "8.13.0",
|
||||
"@tiptap/core": "2.4.0",
|
||||
"@tiptap/extension-blockquote": "2.4.0",
|
||||
"@tiptap/extension-bold": "2.4.0",
|
||||
"@tiptap/extension-bullet-list": "2.4.0",
|
||||
"@tiptap/extension-code": "2.4.0",
|
||||
"@tiptap/extension-code-block-lowlight": "2.4.0",
|
||||
"@tiptap/extension-document": "2.4.0",
|
||||
"@tiptap/extension-dropcursor": "2.4.0",
|
||||
"@tiptap/extension-gapcursor": "2.4.0",
|
||||
"@tiptap/extension-hard-break": "2.4.0",
|
||||
"@tiptap/extension-heading": "2.4.0",
|
||||
"@tiptap/extension-history": "2.4.0",
|
||||
"@tiptap/extension-horizontal-rule": "2.4.0",
|
||||
"@tiptap/extension-image": "2.4.0",
|
||||
"@tiptap/extension-italic": "2.4.0",
|
||||
"@tiptap/extension-link": "2.4.0",
|
||||
"@tiptap/extension-list-item": "2.4.0",
|
||||
"@tiptap/extension-ordered-list": "2.4.0",
|
||||
"@tiptap/extension-paragraph": "2.4.0",
|
||||
"@tiptap/extension-placeholder": "2.4.0",
|
||||
"@tiptap/extension-strike": "2.4.0",
|
||||
"@tiptap/extension-table": "2.4.0",
|
||||
"@tiptap/extension-table-cell": "2.4.0",
|
||||
"@tiptap/extension-table-header": "2.4.0",
|
||||
"@tiptap/extension-table-row": "2.4.0",
|
||||
"@tiptap/extension-task-item": "2.4.0",
|
||||
"@tiptap/extension-task-list": "2.4.0",
|
||||
"@tiptap/extension-text": "2.4.0",
|
||||
"@tiptap/extension-typography": "2.4.0",
|
||||
"@tiptap/extension-underline": "2.4.0",
|
||||
"@tiptap/pm": "2.4.0",
|
||||
"@tiptap/suggestion": "2.4.0",
|
||||
"@tiptap/vue-3": "2.4.0",
|
||||
"@sentry/vue": "8.18.0",
|
||||
"@tiptap/core": "2.5.4",
|
||||
"@tiptap/extension-blockquote": "2.5.4",
|
||||
"@tiptap/extension-bold": "2.5.4",
|
||||
"@tiptap/extension-bullet-list": "2.5.4",
|
||||
"@tiptap/extension-code": "2.5.4",
|
||||
"@tiptap/extension-code-block": "2.5.4",
|
||||
"@tiptap/extension-code-block-lowlight": "2.5.4",
|
||||
"@tiptap/extension-document": "2.5.4",
|
||||
"@tiptap/extension-dropcursor": "2.5.4",
|
||||
"@tiptap/extension-gapcursor": "2.5.4",
|
||||
"@tiptap/extension-hard-break": "2.5.4",
|
||||
"@tiptap/extension-heading": "2.5.4",
|
||||
"@tiptap/extension-history": "2.5.4",
|
||||
"@tiptap/extension-horizontal-rule": "2.5.4",
|
||||
"@tiptap/extension-image": "2.5.4",
|
||||
"@tiptap/extension-italic": "2.5.4",
|
||||
"@tiptap/extension-link": "2.5.4",
|
||||
"@tiptap/extension-list-item": "2.5.4",
|
||||
"@tiptap/extension-ordered-list": "2.5.4",
|
||||
"@tiptap/extension-paragraph": "2.5.4",
|
||||
"@tiptap/extension-placeholder": "2.5.4",
|
||||
"@tiptap/extension-strike": "2.5.4",
|
||||
"@tiptap/extension-table": "2.5.4",
|
||||
"@tiptap/extension-table-cell": "2.5.4",
|
||||
"@tiptap/extension-table-header": "2.5.4",
|
||||
"@tiptap/extension-table-row": "2.5.4",
|
||||
"@tiptap/extension-task-item": "2.5.4",
|
||||
"@tiptap/extension-task-list": "2.5.4",
|
||||
"@tiptap/extension-text": "2.5.4",
|
||||
"@tiptap/extension-typography": "2.5.4",
|
||||
"@tiptap/extension-underline": "2.5.4",
|
||||
"@tiptap/pm": "2.5.4",
|
||||
"@tiptap/suggestion": "2.5.4",
|
||||
"@tiptap/vue-3": "2.5.4",
|
||||
"@vueuse/core": "10.11.0",
|
||||
"@vueuse/router": "10.11.0",
|
||||
"axios": "1.7.2",
|
||||
@@ -100,22 +101,22 @@
|
||||
"bulma-css-variables": "0.9.33",
|
||||
"change-case": "5.4.4",
|
||||
"date-fns": "3.6.0",
|
||||
"dayjs": "1.11.11",
|
||||
"dompurify": "3.1.5",
|
||||
"dayjs": "1.11.12",
|
||||
"dompurify": "3.1.6",
|
||||
"fast-deep-equal": "3.1.3",
|
||||
"flatpickr": "4.6.13",
|
||||
"flexsearch": "0.7.31",
|
||||
"floating-vue": "5.2.2",
|
||||
"is-touch-device": "1.0.1",
|
||||
"klona": "2.0.6",
|
||||
"lodash.debounce": "4.0.8",
|
||||
"lowlight": "2.9.0",
|
||||
"pinia": "2.1.7",
|
||||
"register-service-worker": "1.7.2",
|
||||
"sortablejs": "1.15.2",
|
||||
"tailwindcss": "3.4.4",
|
||||
"tippy.js": "6.3.7",
|
||||
"ufo": "1.5.3",
|
||||
"vue": "3.4.31",
|
||||
"ufo": "1.5.4",
|
||||
"vue": "3.4.32",
|
||||
"vue-advanced-cropper": "2.8.9",
|
||||
"vue-flatpickr-component": "11.0.5",
|
||||
"vue-i18n": "9.13.1",
|
||||
@@ -138,49 +139,48 @@
|
||||
"@types/flexsearch": "0.7.6",
|
||||
"@types/is-touch-device": "1.0.2",
|
||||
"@types/lodash.clonedeep": "4.5.9",
|
||||
"@types/lodash.debounce": "4.0.9",
|
||||
"@types/node": "20.14.9",
|
||||
"@types/node": "20.14.11",
|
||||
"@types/sortablejs": "1.15.8",
|
||||
"@typescript-eslint/eslint-plugin": "7.15.0",
|
||||
"@typescript-eslint/parser": "7.15.0",
|
||||
"@typescript-eslint/eslint-plugin": "7.16.1",
|
||||
"@typescript-eslint/parser": "7.16.1",
|
||||
"@vitejs/plugin-legacy": "5.4.1",
|
||||
"@vitejs/plugin-vue": "5.0.5",
|
||||
"@vue/eslint-config-typescript": "13.0.0",
|
||||
"@vue/test-utils": "2.4.6",
|
||||
"@vue/tsconfig": "0.5.1",
|
||||
"autoprefixer": "10.4.19",
|
||||
"browserslist": "4.23.1",
|
||||
"caniuse-lite": "1.0.30001639",
|
||||
"browserslist": "4.23.2",
|
||||
"caniuse-lite": "1.0.30001642",
|
||||
"csstype": "3.1.3",
|
||||
"cypress": "13.13.0",
|
||||
"cypress": "13.13.1",
|
||||
"esbuild": "0.23.0",
|
||||
"eslint": "8.57.0",
|
||||
"eslint-plugin-vue": "9.26.0",
|
||||
"eslint-plugin-vue": "9.27.0",
|
||||
"happy-dom": "14.12.3",
|
||||
"histoire": "0.17.17",
|
||||
"postcss": "8.4.39",
|
||||
"postcss-easing-gradients": "3.0.1",
|
||||
"postcss-easings": "4.0.0",
|
||||
"postcss-preset-env": "9.5.15",
|
||||
"rollup": "4.18.0",
|
||||
"postcss-preset-env": "9.6.0",
|
||||
"rollup": "4.18.1",
|
||||
"rollup-plugin-visualizer": "5.12.0",
|
||||
"sass": "1.77.6",
|
||||
"sass": "1.77.8",
|
||||
"start-server-and-test": "2.0.4",
|
||||
"typescript": "5.5.3",
|
||||
"unplugin-inject-preload": "2.0.4",
|
||||
"vite": "5.3.2",
|
||||
"vite": "5.3.4",
|
||||
"vite-plugin-pwa": "0.20.0",
|
||||
"vite-plugin-sentry": "1.4.0",
|
||||
"vite-svg-loader": "5.1.0",
|
||||
"vitest": "1.6.0",
|
||||
"vue-tsc": "2.0.24",
|
||||
"vue-tsc": "2.0.26",
|
||||
"wait-on": "7.2.0",
|
||||
"workbox-cli": "7.1.0"
|
||||
},
|
||||
"pnpm": {
|
||||
"patchedDependencies": {
|
||||
"flexsearch@0.7.31": "patches/flexsearch@0.7.31.patch",
|
||||
"@github/hotkey@3.1.0": "patches/@github__hotkey@3.1.0.patch"
|
||||
"@github/hotkey@3.1.1": "patches/@github__hotkey@3.1.1.patch"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+1232
-1071
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<Ready>
|
||||
<template v-if="authUser">
|
||||
<TheNavigation />
|
||||
<AppHeader />
|
||||
<ContentAuth />
|
||||
</template>
|
||||
<ContentLinkShare v-else-if="authLinkShare" />
|
||||
@@ -30,7 +30,7 @@ import Notification from '@/components/misc/Notification.vue'
|
||||
import UpdateNotification from '@/components/home/UpdateNotification.vue'
|
||||
import KeyboardShortcuts from '@/components/misc/keyboard-shortcuts/index.vue'
|
||||
|
||||
import TheNavigation from '@/components/home/TheNavigation.vue'
|
||||
import AppHeader from '@/components/home/AppHeader.vue'
|
||||
import ContentAuth from '@/components/home/ContentAuth.vue'
|
||||
import ContentLinkShare from '@/components/home/ContentLinkShare.vue'
|
||||
import NoAuthWrapper from '@/components/misc/NoAuthWrapper.vue'
|
||||
|
||||
@@ -72,7 +72,7 @@ export interface BaseButtonProps extends /* @vue-ignore */ HTMLAttributes {
|
||||
openExternalInNewTab?: boolean
|
||||
}
|
||||
|
||||
export interface BaseButtonEmits {
|
||||
export type BaseButtonEmits = {
|
||||
click: [payload: MouseEvent]
|
||||
}
|
||||
|
||||
|
||||
@@ -114,13 +114,19 @@ import BaseButton from '@/components/base/BaseButton.vue'
|
||||
import DatemathHelp from '@/components/date/DatemathHelp.vue'
|
||||
import {getFlatpickrLanguage} from '@/helpers/flatpickrLanguage'
|
||||
|
||||
const props = defineProps({
|
||||
const props = defineProps<{
|
||||
modelValue: {
|
||||
required: false,
|
||||
dateFrom: Date | string,
|
||||
dateTo: Date | string,
|
||||
},
|
||||
})
|
||||
}>()
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [value: {
|
||||
dateFrom: Date | string,
|
||||
dateTo: Date | string
|
||||
}]
|
||||
}>()
|
||||
|
||||
const {t} = useI18n({useScope: 'global'})
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="datepicker-with-range-container">
|
||||
<Popup
|
||||
:open="open"
|
||||
@close="() => emit('close')"
|
||||
@update:open="(open) => !open && $emit('update:open', false)"
|
||||
>
|
||||
<template #content="{isOpen}">
|
||||
<div
|
||||
@@ -91,18 +91,18 @@ import BaseButton from '@/components/base/BaseButton.vue'
|
||||
import DatemathHelp from '@/components/date/DatemathHelp.vue'
|
||||
import {getFlatpickrLanguage} from '@/helpers/flatpickrLanguage'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
open: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
const props = withDefaults(defineProps<{
|
||||
modelValue: string | Date | null,
|
||||
open: boolean
|
||||
}>(), {
|
||||
modelValue: null,
|
||||
open: false,
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue', 'close'])
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [value: string | Date | null],
|
||||
'update:open': [open: boolean],
|
||||
}>()
|
||||
|
||||
const {t} = useI18n({useScope: 'global'})
|
||||
|
||||
|
||||
+14
-7
@@ -70,7 +70,7 @@
|
||||
>
|
||||
<span class="username">{{ authStore.userDisplayName }}</span>
|
||||
<span
|
||||
class="icon is-small"
|
||||
class="ml-1 dropdown-icon icon is-small"
|
||||
:style="{
|
||||
transform: open ? 'rotate(180deg)' : 'rotate(0)',
|
||||
}"
|
||||
@@ -161,13 +161,8 @@ $user-dropdown-width-mobile: 5rem;
|
||||
|
||||
background: var(--site-background);
|
||||
|
||||
@media screen and (max-width: $tablet) {
|
||||
padding-right: .5rem;
|
||||
}
|
||||
|
||||
@media screen and (min-width: $tablet) {
|
||||
padding-left: 2rem;
|
||||
padding-right: 1rem;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
@@ -263,11 +258,19 @@ $user-dropdown-width-mobile: 5rem;
|
||||
}
|
||||
|
||||
.username-dropdown-trigger {
|
||||
padding-left: 1rem;
|
||||
padding-left: .75rem;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
font-size: .85rem;
|
||||
font-weight: 700;
|
||||
|
||||
@media screen and (max-width: $tablet) {
|
||||
padding-right: .5rem;
|
||||
}
|
||||
|
||||
@media screen and (min-width: $tablet) {
|
||||
padding-right: .75rem;
|
||||
}
|
||||
}
|
||||
|
||||
.username {
|
||||
@@ -278,6 +281,10 @@ $user-dropdown-width-mobile: 5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-icon {
|
||||
transition: transform $transition;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
border-radius: 100%;
|
||||
vertical-align: middle;
|
||||
@@ -160,6 +160,7 @@ projectStore.loadAllProjects()
|
||||
}
|
||||
|
||||
.app-content {
|
||||
display: flow-root;
|
||||
z-index: 10;
|
||||
position: relative;
|
||||
padding: 1.5rem 0.5rem 0;
|
||||
|
||||
@@ -109,7 +109,7 @@
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<PoweredByLink />
|
||||
<PoweredByLink class="mt-auto" />
|
||||
</aside>
|
||||
</template>
|
||||
|
||||
@@ -146,6 +146,8 @@ const savedFilterProjects = computed(() => projectStore.savedFilterProjects)
|
||||
}
|
||||
|
||||
.menu-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: var(--site-background);
|
||||
color: $vikunja-nav-color;
|
||||
padding: 1rem 0;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
'has-no-shadow': !shadow || variant === 'tertiary',
|
||||
}
|
||||
]"
|
||||
:disabled="disabled || loading"
|
||||
:style="{
|
||||
'--button-white-space': wrap ? 'break-spaces' : 'nowrap',
|
||||
}"
|
||||
@@ -44,6 +45,7 @@ export interface ButtonProps extends /* @vue-ignore */ BaseButtonProps {
|
||||
icon?: IconProp
|
||||
iconColor?: string
|
||||
loading?: boolean
|
||||
disabled?: boolean
|
||||
shadow?: boolean
|
||||
wrap?: boolean
|
||||
}
|
||||
@@ -53,6 +55,7 @@ const props = withDefaults(defineProps<ButtonProps>(), {
|
||||
icon: undefined,
|
||||
iconColor: undefined,
|
||||
loading: false,
|
||||
disabled: false,
|
||||
shadow: true,
|
||||
wrap: true,
|
||||
})
|
||||
|
||||
@@ -70,13 +70,7 @@ import {computed, ref, watch} from 'vue'
|
||||
import {createRandomID} from '@/helpers/randomId'
|
||||
import XButton from '@/components/input/Button.vue'
|
||||
|
||||
const {
|
||||
modelValue,
|
||||
} = defineProps<{
|
||||
modelValue: string,
|
||||
}>()
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const model = defineModel<string>({ required: true })
|
||||
|
||||
const DEFAULT_COLORS = [
|
||||
'#1973ff',
|
||||
@@ -93,7 +87,7 @@ const defaultColors = ref(DEFAULT_COLORS)
|
||||
const colorListID = ref(createRandomID())
|
||||
|
||||
watch(
|
||||
() => modelValue,
|
||||
model,
|
||||
(newValue) => {
|
||||
if (newValue === '' || newValue.startsWith('var(')) {
|
||||
color.value = ''
|
||||
@@ -123,7 +117,7 @@ function update(force = false) {
|
||||
}
|
||||
|
||||
lastChangeTimeout.value = setTimeout(() => {
|
||||
emit('update:modelValue', color.value)
|
||||
model.value = color.value
|
||||
}, 500)
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {ref, onMounted, onBeforeUnmount, toRef, watch, type PropType} from 'vue'
|
||||
import {ref, onMounted, onBeforeUnmount, toRef, watch} from 'vue'
|
||||
|
||||
import CustomTransition from '@/components/misc/CustomTransition.vue'
|
||||
import DatepickerInline from '@/components/input/DatepickerInline.vue'
|
||||
@@ -44,26 +44,24 @@ import {closeWhenClickedOutside} from '@/helpers/closeWhenClickedOutside'
|
||||
import {createDateFromString} from '@/helpers/time/createDateFromString'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: [Date, null, String] as PropType<Date | null | string>,
|
||||
validator: prop => prop instanceof Date || prop === null || typeof prop === 'string',
|
||||
default: null,
|
||||
},
|
||||
chooseDateLabel: {
|
||||
type: String,
|
||||
default() {
|
||||
const {t} = useI18n({useScope: 'global'})
|
||||
return t('input.datepicker.chooseDate')
|
||||
},
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
const props = withDefaults(defineProps<{
|
||||
modelValue: Date | null | string,
|
||||
chooseDateLabel: string,
|
||||
disabled?: boolean,
|
||||
}>(), {
|
||||
modelValue: null,
|
||||
chooseDateLabel: () => {
|
||||
const {t} = useI18n({useScope: 'global'})
|
||||
return t('input.datepicker.chooseDate')
|
||||
},
|
||||
disabled: false,
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue', 'close', 'closeOnChange'])
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [value: Date | null],
|
||||
'close': [value: boolean],
|
||||
'closeOnChange': [value: boolean],
|
||||
}>()
|
||||
|
||||
const date = ref<Date | null>()
|
||||
const show = ref(false)
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {computed, onBeforeUnmount, onMounted, type PropType, ref, toRef, watch} from 'vue'
|
||||
import {computed, onBeforeUnmount, onMounted, ref, toRef, watch} from 'vue'
|
||||
import flatPickr from 'vue-flatpickr-component'
|
||||
import 'flatpickr/dist/flatpickr.css'
|
||||
|
||||
@@ -84,15 +84,15 @@ import {createDateFromString} from '@/helpers/time/createDateFromString'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
import {getFlatpickrLanguage} from '@/helpers/flatpickrLanguage'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: [Date, null, String] as PropType<Date | null | string>,
|
||||
validator: prop => prop instanceof Date || prop === null || typeof prop === 'string',
|
||||
default: null,
|
||||
},
|
||||
const props = withDefaults(defineProps<{
|
||||
modelValue: Date | null | string
|
||||
}>(), {
|
||||
modelValue: null,
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue', 'close-on-change'])
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [Date | null],
|
||||
}>()
|
||||
|
||||
const {t} = useI18n({useScope: 'global'})
|
||||
|
||||
|
||||
@@ -21,24 +21,20 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import CheckboxIcon from '@/assets/checkbox.svg?component'
|
||||
|
||||
import BaseCheckbox from '@/components/base/BaseCheckbox.vue'
|
||||
|
||||
defineProps({
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
},
|
||||
isBlock: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
withDefaults(defineProps<{
|
||||
modelValue: boolean,
|
||||
disabled?: boolean,
|
||||
isBlock?: boolean
|
||||
}>(), {
|
||||
modelValue: false,
|
||||
disabled: false,
|
||||
isBlock: false,
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
(event: 'update:modelValue', value: boolean): void
|
||||
'update:modelValue': [value: boolean]
|
||||
}>()
|
||||
</script>
|
||||
|
||||
|
||||
@@ -40,10 +40,12 @@
|
||||
</slot>
|
||||
|
||||
<input
|
||||
:id="id"
|
||||
ref="searchInput"
|
||||
v-model="query"
|
||||
type="text"
|
||||
class="input"
|
||||
:name="name"
|
||||
:placeholder="placeholder"
|
||||
:autocomplete="autocompleteEnabled ? undefined : 'off'"
|
||||
:spellcheck="autocompleteEnabled ? undefined : 'false'"
|
||||
@@ -119,9 +121,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
computed, onBeforeUnmount, onMounted, ref, toRefs, watch, type ComponentPublicInstance, type PropType,
|
||||
} from 'vue'
|
||||
import {computed, onBeforeUnmount, onMounted, ref, toRefs, watch, type ComponentPublicInstance} from 'vue'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
|
||||
import {closeWhenClickedOutside} from '@/helpers/closeWhenClickedOutside'
|
||||
@@ -129,132 +129,79 @@ import {closeWhenClickedOutside} from '@/helpers/closeWhenClickedOutside'
|
||||
import BaseButton from '@/components/base/BaseButton.vue'
|
||||
import CustomTransition from '@/components/misc/CustomTransition.vue'
|
||||
|
||||
const props = defineProps({
|
||||
/**
|
||||
* When true, shows a loading spinner
|
||||
*/
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
/**
|
||||
* The placeholder of the search input
|
||||
*/
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
/**
|
||||
* The search results where the @search listener needs to put the results into
|
||||
*/
|
||||
searchResults: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
type: Array as PropType<{ [id: string]: any }>,
|
||||
default: () => [],
|
||||
},
|
||||
/**
|
||||
* The name of the property of the searched object to show the user.
|
||||
* If empty the component will show all raw data of an entry.
|
||||
*/
|
||||
label: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
/**
|
||||
* The object with the value, updated every time an entry is selected.
|
||||
*/
|
||||
modelValue: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
type: [Object] as PropType<{ [key: string]: any }>,
|
||||
default: null,
|
||||
},
|
||||
/**
|
||||
* If true, will provide an "add this as a new value" entry which fires an @create event when clicking on it.
|
||||
*/
|
||||
creatable: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
/**
|
||||
* The text shown next to the new value option.
|
||||
*/
|
||||
createPlaceholder: {
|
||||
type: String,
|
||||
default() {
|
||||
const {t} = useI18n({useScope: 'global'})
|
||||
return t('input.multiselect.createPlaceholder')
|
||||
},
|
||||
},
|
||||
/**
|
||||
* The text shown next to an option.
|
||||
*/
|
||||
selectPlaceholder: {
|
||||
type: String,
|
||||
default() {
|
||||
const {t} = useI18n({useScope: 'global'})
|
||||
return t('input.multiselect.selectPlaceholder')
|
||||
},
|
||||
},
|
||||
/**
|
||||
* If true, allows for selecting multiple items. v-model will be an array with all selected values in that case.
|
||||
*/
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
/**
|
||||
* If true, displays the search results inline instead of using a dropdown.
|
||||
*/
|
||||
inline: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
/**
|
||||
* If true, shows search results when no query is specified.
|
||||
*/
|
||||
showEmpty: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
/**
|
||||
* The delay in ms after which the search event will be fired. Used to avoid hitting the network on every keystroke.
|
||||
*/
|
||||
searchDelay: {
|
||||
type: Number,
|
||||
default: 200,
|
||||
},
|
||||
closeAfterSelect: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
/**
|
||||
* If false, the search input will get the autocomplete="off" attributes attached to it.
|
||||
*/
|
||||
autocompleteEnabled: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
const props = withDefaults(defineProps<{
|
||||
/** The object with the value, updated every time an entry is selected */
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
modelValue: { [id: string]: any } | null
|
||||
/** When true, shows a loading spinner */
|
||||
loading?: boolean
|
||||
/** The placeholder of the search input */
|
||||
placeholder?: string
|
||||
/** The search results where the @search listener needs to put the results into */
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
searchResults?: { [id: string]: any }[]
|
||||
/** The name of the property of the searched object to show the user. If empty the component will show all raw data of an entry */
|
||||
label?: string
|
||||
/** The id attribute of the input element */
|
||||
id?: string
|
||||
/** The name attribute of the input element */
|
||||
name?: string
|
||||
/** If true, will provide an 'add this as a new value' entry which fires an @create event when clicking on it. */
|
||||
creatable?: boolean
|
||||
/** The text shown next to the new value option. */
|
||||
createPlaceholder?: string
|
||||
/** The text shown next to an option. */
|
||||
selectPlaceholder: string
|
||||
/** If true, allows for selecting multiple items. v-model will be an array with all selected values in that case. */
|
||||
multiple?: boolean
|
||||
/** If true, displays the search results inline instead of using a dropdown. */
|
||||
inline?: boolean
|
||||
/** If true, shows search results when no query is specified. */
|
||||
showEmpty?: boolean
|
||||
/** The delay in ms after which the search event will be fired. Used to avoid hitting the network on every keystroke. */
|
||||
searchDelay?: number
|
||||
/** If true, closes the dropdown after an entry is selected */
|
||||
closeAfterSelect?: boolean
|
||||
/** If false, the search input will get the autocomplete="off" attributes attached to it. */
|
||||
autocompleteEnabled?: boolean
|
||||
}>(), {
|
||||
modelValue: null,
|
||||
loading: false,
|
||||
placeholder: '',
|
||||
searchResults: () => [],
|
||||
label: '',
|
||||
creatable: false,
|
||||
createPlaceholder: () => useI18n().t('input.multiselect.createPlaceholder'),
|
||||
selectPlaceholder: () => useI18n().t('input.multiselect.selectPlaceholder'),
|
||||
multiple: false,
|
||||
inline: false,
|
||||
showEmpty: false,
|
||||
searchDelay: 200,
|
||||
closeAfterSelect: true,
|
||||
autocompleteEnabled: true,
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', value: null): void
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
'update:modelValue': [value: { [key: string]: any }[] | null],
|
||||
/**
|
||||
* Triggered every time the search query input changes
|
||||
*/
|
||||
(e: 'search', query: string): void
|
||||
'search': [query: string],
|
||||
/**
|
||||
* Triggered every time an option from the search results is selected. Also triggers a change in v-model.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(e: 'select', value: { [key: string]: any }): void
|
||||
'select': [value: { [key: string]: any }],
|
||||
/**
|
||||
* If nothing or no exact match was found and `creatable` is true, this event is triggered with the current value of the search query.
|
||||
*/
|
||||
(e: 'create', query: string): void
|
||||
'create': [query: string],
|
||||
/**
|
||||
* If `multiple` is enabled, this will be fired every time an item is removed from the array of selected items.
|
||||
*/
|
||||
(e: 'remove', value: null): void
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
'remove': [value: { [key: string]: any }],
|
||||
}>()
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@@ -324,7 +271,7 @@ const hasMultiple = computed(() => {
|
||||
return props.multiple && Array.isArray(internalValue.value) && internalValue.value.length > 0
|
||||
})
|
||||
|
||||
const removalAvailable = computed(() => !props.multiple && internalValue.value !== null && query.value !== '')
|
||||
const removalAvailable = computed(() => !props.multiple && internalValue.value !== null && query.value !== '' && !(props.loading || localLoading.value))
|
||||
function resetSelectedValue() {
|
||||
select(null)
|
||||
}
|
||||
|
||||
@@ -32,33 +32,32 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {ref, watch} from 'vue'
|
||||
import {ref, watchEffect} from 'vue'
|
||||
import {useDebounceFn} from '@vueuse/core'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
import BaseButton from '@/components/base/BaseButton.vue'
|
||||
|
||||
const props = defineProps({
|
||||
tabindex: String,
|
||||
modelValue: String,
|
||||
const props = withDefaults(defineProps<{
|
||||
modelValue: string,
|
||||
tabindex?: string,
|
||||
// This prop is a workaround to trigger validation from the outside when the user never had focus in the input.
|
||||
validateInitially: Boolean,
|
||||
validateMinLength: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
validateInitially?: boolean,
|
||||
validateMinLength?: boolean,
|
||||
}>(), {
|
||||
validateMinLength: true,
|
||||
})
|
||||
const emit = defineEmits(['submit', 'update:modelValue'])
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [value: string],
|
||||
'submit': [event: Event],
|
||||
}>()
|
||||
const {t} = useI18n()
|
||||
const passwordFieldType = ref('password')
|
||||
const password = ref('')
|
||||
const isValid = ref<true | string>(props.validateInitially === true ? true : '')
|
||||
const validateAfterFirst = ref(false)
|
||||
|
||||
watch(
|
||||
() => props.validateInitially,
|
||||
() => props.validateInitially && validate(),
|
||||
{immediate: true},
|
||||
)
|
||||
watchEffect(() => props.validateInitially && validate())
|
||||
|
||||
const validate = useDebounceFn(() => {
|
||||
if (password.value === '') {
|
||||
|
||||
@@ -13,27 +13,25 @@ import {closeWhenClickedOutside} from '@/helpers/closeWhenClickedOutside'
|
||||
import {useAuthStore} from '@/stores/auth'
|
||||
import {useColorScheme} from '@/composables/useColorScheme'
|
||||
|
||||
const {
|
||||
entityKind,
|
||||
entityId,
|
||||
disabled = false,
|
||||
} = defineProps<{
|
||||
const props = withDefaults(defineProps<{
|
||||
entityKind: ReactionKind,
|
||||
entityId: number,
|
||||
disabled?: boolean,
|
||||
}>()
|
||||
}>(), {
|
||||
disabled: false,
|
||||
})
|
||||
|
||||
const model = defineModel<IReactionPerEntity>()
|
||||
|
||||
const authStore = useAuthStore()
|
||||
const {t} = useI18n()
|
||||
const reactionService = new ReactionService()
|
||||
const {isDark} = useColorScheme()
|
||||
|
||||
const model = defineModel<IReactionPerEntity>()
|
||||
|
||||
async function addReaction(value: string) {
|
||||
const reaction = new ReactionModel({
|
||||
id: entityId,
|
||||
kind: entityKind,
|
||||
id: props.entityId,
|
||||
kind: props.entityKind,
|
||||
value,
|
||||
})
|
||||
await reactionService.create(reaction)
|
||||
@@ -52,8 +50,8 @@ async function addReaction(value: string) {
|
||||
|
||||
async function removeReaction(value: string) {
|
||||
const reaction = new ReactionModel({
|
||||
id: entityId,
|
||||
kind: entityKind,
|
||||
id: props.entityId,
|
||||
kind: props.entityKind,
|
||||
value,
|
||||
})
|
||||
await reactionService.delete(reaction)
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {computed, ref, shallowReactive, watchEffect, type PropType} from 'vue'
|
||||
import {computed, ref, shallowReactive, watchEffect} from 'vue'
|
||||
|
||||
import Multiselect from '@/components/input/Multiselect.vue'
|
||||
|
||||
@@ -20,23 +20,16 @@ import type {IProject} from '@/modelTypes/IProject'
|
||||
import ProjectService from '@/services/project'
|
||||
import {includesById} from '@/helpers/utils'
|
||||
|
||||
type ProjectFilterFunc = (p: IProject) => boolean
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Array as PropType<IProject[]>,
|
||||
default: () => [],
|
||||
},
|
||||
projectFilter: {
|
||||
type: Function as PropType<ProjectFilterFunc>,
|
||||
default: () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
return (_: IProject) => true
|
||||
},
|
||||
},
|
||||
const props = withDefaults(defineProps<{
|
||||
modelValue: IProject[],
|
||||
projectFilter: (p: IProject) => boolean
|
||||
}>(), {
|
||||
modelValue: () => [],
|
||||
projectFilter: () => () => true,
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', value: IProject[]): void
|
||||
'update:modelValue': [value: IProject[]]
|
||||
}>()
|
||||
|
||||
const projects = ref<IProject[]>([])
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {computed, ref, shallowReactive, watchEffect, type PropType} from 'vue'
|
||||
import {computed, ref, shallowReactive, watchEffect} from 'vue'
|
||||
|
||||
import Multiselect from '@/components/input/Multiselect.vue'
|
||||
|
||||
@@ -20,14 +20,14 @@ import type {IUser} from '@/modelTypes/IUser'
|
||||
import UserService from '@/services/user'
|
||||
import {includesById} from '@/helpers/utils'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Array as PropType<IUser[]>,
|
||||
default: () => [],
|
||||
},
|
||||
const props = withDefaults(defineProps<{
|
||||
modelValue: IUser[] | undefined
|
||||
}>(), {
|
||||
modelValue: () => [],
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', value: IUser[]): void
|
||||
'update:modelValue': [value: IUser[]]
|
||||
}>()
|
||||
|
||||
const users = ref<IUser[]>([])
|
||||
|
||||
@@ -325,6 +325,13 @@ watch(
|
||||
},
|
||||
)
|
||||
|
||||
const additionalLinkProtocols = [
|
||||
'ftp',
|
||||
'git',
|
||||
'obsidian',
|
||||
'notion',
|
||||
]
|
||||
|
||||
const extensions : Extensions = [
|
||||
// Starterkit:
|
||||
Blockquote,
|
||||
@@ -379,7 +386,11 @@ const extensions : Extensions = [
|
||||
Underline,
|
||||
Link.configure({
|
||||
openOnClick: false,
|
||||
validate: (href: string) => /^https?:\/\//.test(href),
|
||||
validate: (href: string) => (new RegExp(
|
||||
`^(https?|${additionalLinkProtocols.join('|')}):\\/\\/`,
|
||||
'i',
|
||||
)).test(href),
|
||||
protocols: additionalLinkProtocols,
|
||||
}),
|
||||
Table.configure({
|
||||
resizable: true,
|
||||
@@ -696,6 +707,9 @@ watch(
|
||||
|
||||
<style lang="scss">
|
||||
.tiptap__editor {
|
||||
transition: box-shadow $transition;
|
||||
border-radius: $radius;
|
||||
|
||||
&.tiptap__editor-is-edit-enabled {
|
||||
min-height: 10rem;
|
||||
|
||||
@@ -711,9 +725,6 @@ watch(
|
||||
cursor: text;
|
||||
}
|
||||
}
|
||||
|
||||
transition: box-shadow $transition;
|
||||
border-radius: $radius;
|
||||
}
|
||||
|
||||
.tiptap p::before {
|
||||
|
||||
@@ -71,14 +71,14 @@ import {success} from '@/message'
|
||||
import Message from '@/components/misc/Message.vue'
|
||||
import ButtonLink from '@/components/misc/ButtonLink.vue'
|
||||
|
||||
const props = defineProps({
|
||||
configureOpen: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
const props = withDefaults(defineProps<{
|
||||
configureOpen?: boolean
|
||||
}>(), {
|
||||
configureOpen: false,
|
||||
})
|
||||
const emit = defineEmits(['foundApi'])
|
||||
const emit = defineEmits<{
|
||||
'foundApi': [url: string],
|
||||
}>()
|
||||
|
||||
const apiUrl = ref(window.API_URL)
|
||||
const configureApi = ref(window.API_URL === '')
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
{{ title }}
|
||||
</p>
|
||||
<BaseButton
|
||||
v-if="hasClose"
|
||||
v-if="$attrs.onClose"
|
||||
v-tooltip="$t('misc.close')"
|
||||
class="card-header-icon"
|
||||
:aria-label="$t('misc.close')"
|
||||
@@ -51,7 +51,6 @@ import BaseButton from '@/components/base/BaseButton.vue'
|
||||
withDefaults(defineProps<{
|
||||
title?: string
|
||||
padding?: boolean
|
||||
hasClose?: boolean
|
||||
closeIcon?: IconProp
|
||||
shadow?: boolean
|
||||
hasContent?: boolean
|
||||
@@ -59,14 +58,15 @@ withDefaults(defineProps<{
|
||||
}>(), {
|
||||
title: '',
|
||||
padding: true,
|
||||
hasClose: false,
|
||||
closeIcon: 'times',
|
||||
shadow: true,
|
||||
hasContent: true,
|
||||
loading: false,
|
||||
})
|
||||
|
||||
defineEmits(['close'])
|
||||
defineEmits<{
|
||||
'close': []
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
:shadow="false"
|
||||
:padding="false"
|
||||
class="has-text-left"
|
||||
:has-close="true"
|
||||
:loading="loading"
|
||||
@close="$router.back()"
|
||||
>
|
||||
@@ -23,7 +22,7 @@
|
||||
v-if="tertiary !== ''"
|
||||
:shadow="false"
|
||||
variant="tertiary"
|
||||
@click.prevent.stop="$emit('tertiary')"
|
||||
@click.prevent.stop="$emit('tertiary', $event)"
|
||||
>
|
||||
{{ tertiary }}
|
||||
</x-button>
|
||||
@@ -39,7 +38,7 @@
|
||||
:icon="primaryIcon"
|
||||
:disabled="primaryDisabled || loading"
|
||||
class="ml-2"
|
||||
@click.prevent.stop="primary()"
|
||||
@click.prevent.stop="primary"
|
||||
>
|
||||
{{ primaryLabel || $t('misc.create') }}
|
||||
</x-button>
|
||||
@@ -71,10 +70,14 @@ withDefaults(defineProps<{
|
||||
loading: false,
|
||||
})
|
||||
|
||||
const emit = defineEmits(['create', 'primary', 'tertiary'])
|
||||
const emit = defineEmits<{
|
||||
'create': [event: MouseEvent],
|
||||
'primary': [event: MouseEvent],
|
||||
'tertiary': [event: MouseEvent]
|
||||
}>()
|
||||
|
||||
function primary() {
|
||||
emit('create')
|
||||
emit('primary')
|
||||
function primary(event: MouseEvent) {
|
||||
emit('create', event)
|
||||
emit('primary', event)
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
<div
|
||||
ref="dropdown"
|
||||
class="dropdown"
|
||||
@pointerenter="initialMount = true"
|
||||
>
|
||||
<slot
|
||||
name="trigger"
|
||||
@@ -22,7 +23,8 @@
|
||||
|
||||
<CustomTransition name="fade">
|
||||
<div
|
||||
v-if="open"
|
||||
v-if="initialMount || open"
|
||||
v-show="open"
|
||||
class="dropdown-menu"
|
||||
>
|
||||
<div class="dropdown-content">
|
||||
@@ -34,21 +36,34 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {ref, type PropType} from 'vue'
|
||||
import {ref} from 'vue'
|
||||
import {onClickOutside} from '@vueuse/core'
|
||||
import type {IconProp} from '@fortawesome/fontawesome-svg-core'
|
||||
|
||||
import CustomTransition from '@/components/misc/CustomTransition.vue'
|
||||
import BaseButton from '@/components/base/BaseButton.vue'
|
||||
|
||||
defineProps({
|
||||
triggerIcon: {
|
||||
type: String as PropType<IconProp>,
|
||||
default: 'ellipsis-h',
|
||||
},
|
||||
withDefaults(defineProps<{
|
||||
triggerIcon?: IconProp
|
||||
}>(), {
|
||||
triggerIcon: 'ellipsis-h',
|
||||
})
|
||||
const emit = defineEmits(['close'])
|
||||
|
||||
const emit = defineEmits<{
|
||||
'close': [event: PointerEvent]
|
||||
}>()
|
||||
|
||||
defineSlots<{
|
||||
'trigger': (props: {
|
||||
close: () => void,
|
||||
toggleOpen: () => void,
|
||||
open: boolean
|
||||
}) => void,
|
||||
'default': () => void
|
||||
}>()
|
||||
|
||||
|
||||
const initialMount = ref(false)
|
||||
const open = ref(false)
|
||||
|
||||
function close() {
|
||||
@@ -60,7 +75,7 @@ function toggleOpen() {
|
||||
}
|
||||
|
||||
const dropdown = ref()
|
||||
onClickOutside(dropdown, (e: Event) => {
|
||||
onClickOutside(dropdown, (e) => {
|
||||
if (!open.value) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<template>
|
||||
<BaseButton
|
||||
class="dropdown-item"
|
||||
:disabled="disabled"
|
||||
:class="{'is-disabled': disabled}"
|
||||
>
|
||||
<span
|
||||
|
||||
@@ -28,7 +28,7 @@ import {
|
||||
faDownload,
|
||||
faEllipsisH,
|
||||
faEllipsisV,
|
||||
faExclamation,
|
||||
faExclamationCircle,
|
||||
faEye,
|
||||
faEyeSlash,
|
||||
faFile,
|
||||
@@ -138,7 +138,7 @@ library.add(faCopy)
|
||||
library.add(faDownload)
|
||||
library.add(faEllipsisH)
|
||||
library.add(faEllipsisV)
|
||||
library.add(faExclamation)
|
||||
library.add(faExclamationCircle)
|
||||
library.add(faEye)
|
||||
library.add(faEyeSlash)
|
||||
library.add(faFillDrip)
|
||||
|
||||
@@ -53,11 +53,11 @@ import ApiConfig from '@/components/misc/ApiConfig.vue'
|
||||
import {useTitle} from '@/composables/useTitle'
|
||||
import {useConfigStore} from '@/stores/config'
|
||||
|
||||
const {
|
||||
showApiConfig = true,
|
||||
} = defineProps<{
|
||||
withDefaults(defineProps<{
|
||||
showApiConfig?: boolean
|
||||
}>()
|
||||
}>(), {
|
||||
showApiConfig: true,
|
||||
})
|
||||
const configStore = useConfigStore()
|
||||
const motd = computed(() => configStore.motd)
|
||||
|
||||
|
||||
@@ -45,15 +45,11 @@
|
||||
<script lang="ts" setup>
|
||||
import {computed} from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
totalPages: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
currentPage: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
const props = withDefaults(defineProps<{
|
||||
totalPages: number,
|
||||
currentPage?: number
|
||||
}>(), {
|
||||
currentPage: 0,
|
||||
})
|
||||
|
||||
function createPagination(totalPages: number, currentPage: number) {
|
||||
|
||||
@@ -34,7 +34,22 @@ const props = withDefaults(defineProps<{
|
||||
open: false,
|
||||
})
|
||||
|
||||
const emit = defineEmits(['close'])
|
||||
const emit = defineEmits<{
|
||||
'update:open': [open: boolean]
|
||||
}>()
|
||||
|
||||
defineSlots<{
|
||||
trigger(props: {
|
||||
isOpen: boolean,
|
||||
toggle: () => boolean,
|
||||
close: () => void,
|
||||
}) : void
|
||||
content(props: {
|
||||
isOpen: boolean,
|
||||
toggle: () => boolean,
|
||||
close: () => void
|
||||
}): void
|
||||
}>()
|
||||
|
||||
const openValue = ref(props.open)
|
||||
watchEffect(() => {
|
||||
@@ -42,22 +57,22 @@ watchEffect(() => {
|
||||
})
|
||||
|
||||
function close() {
|
||||
if (!openValue.value) {
|
||||
return
|
||||
}
|
||||
openValue.value = false
|
||||
emit('close')
|
||||
emit('update:open', false)
|
||||
}
|
||||
|
||||
function toggle() {
|
||||
openValue.value = !openValue.value
|
||||
emit('update:open', openValue.value)
|
||||
return openValue.value
|
||||
}
|
||||
|
||||
const popup = ref<HTMLElement | null>(null)
|
||||
|
||||
onClickOutside(popup, () => {
|
||||
if (!openValue.value) {
|
||||
return
|
||||
}
|
||||
close()
|
||||
})
|
||||
onClickOutside(popup, () => close())
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -13,19 +13,13 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
defineProps({
|
||||
value: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
isSmall: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
isPrimary: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
},
|
||||
withDefaults(defineProps<{
|
||||
value: number
|
||||
isSmall?: boolean
|
||||
isPrimary?: boolean
|
||||
}>(), {
|
||||
isSmall: false,
|
||||
isPrimary: false,
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -47,18 +47,20 @@ import {success} from '@/message'
|
||||
import type { IconProp } from '@fortawesome/fontawesome-svg-core'
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
modelValue: ISubscription | null,
|
||||
entity: ISubscription['entity'],
|
||||
entityId: number,
|
||||
isButton?: boolean,
|
||||
modelValue?: ISubscription,
|
||||
type?: 'button' | 'dropdown',
|
||||
}>(), {
|
||||
isButton: true,
|
||||
modelValue: null,
|
||||
isButton: true,
|
||||
type: 'button',
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [subscription: ISubscription | null]
|
||||
}>()
|
||||
|
||||
const subscriptionEntity = computed<string | null>(() => props.modelValue?.entity ?? null)
|
||||
|
||||
@@ -91,7 +93,7 @@ const tooltipText = computed(() => {
|
||||
|
||||
const buttonText = computed(() => props.modelValue ? t('task.subscription.unsubscribe') : t('task.subscription.subscribe'))
|
||||
const iconName = computed<IconProp>(() => props.modelValue ? ['far', 'bell-slash'] : 'bell')
|
||||
const disabled = computed(() => props.modelValue && subscriptionEntity.value !== props.entity)
|
||||
const disabled = computed(() => props.modelValue && subscriptionEntity.value !== props.entity || false)
|
||||
|
||||
function changeSubscription() {
|
||||
if (disabled.value) {
|
||||
|
||||
@@ -16,13 +16,13 @@
|
||||
class="switch-view"
|
||||
>
|
||||
<BaseButton
|
||||
v-for="v in views"
|
||||
:key="v.id"
|
||||
v-for="view in views"
|
||||
:key="view.id"
|
||||
class="switch-view-button"
|
||||
:class="{'is-active': v.id === viewId}"
|
||||
:to="{ name: 'project.view', params: { projectId, viewId: v.id } }"
|
||||
:class="{'is-active': view.id === viewId}"
|
||||
:to="{ name: 'project.view', params: { projectId, viewId: view.id } }"
|
||||
>
|
||||
{{ getViewTitle(v) }}
|
||||
{{ getViewTitle(view) }}
|
||||
</BaseButton>
|
||||
</div>
|
||||
<slot name="header" />
|
||||
@@ -62,10 +62,7 @@ import type {IProject} from '@/modelTypes/IProject'
|
||||
import type {IProjectView} from '@/modelTypes/IProjectView'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
|
||||
const {
|
||||
projectId,
|
||||
viewId,
|
||||
} = defineProps<{
|
||||
const props = defineProps<{
|
||||
projectId: IProject['id'],
|
||||
viewId: IProjectView['id'],
|
||||
}>()
|
||||
@@ -88,7 +85,7 @@ const currentProject = computed<IProject>(() => {
|
||||
})
|
||||
useTitle(() => currentProject.value?.id ? getProjectTitle(currentProject.value) : '')
|
||||
|
||||
const views = computed(() => projectStore.projects[projectId]?.views)
|
||||
const views = computed(() => projectStore.projects[props.projectId]?.views)
|
||||
|
||||
// watchEffect would be called every time the prop would get a value assigned, even if that value was the same as before.
|
||||
// This resulted in loading and setting the project multiple times, even when navigating away from it.
|
||||
@@ -96,7 +93,7 @@ const views = computed(() => projectStore.projects[projectId]?.views)
|
||||
// project background and then navigating to home. It also highlighted the project in the menu and didn't allow changing any
|
||||
// of it, most likely due to the rights not being properly populated.
|
||||
watch(
|
||||
() => projectId,
|
||||
() => props.projectId,
|
||||
// loadProject
|
||||
async (projectIdToLoad: number) => {
|
||||
const projectData = {id: projectIdToLoad}
|
||||
@@ -112,7 +109,7 @@ watch(
|
||||
)
|
||||
&& typeof currentProject.value !== 'undefined' && currentProject.value.maxRight !== null
|
||||
) {
|
||||
loadedProjectId.value = projectId
|
||||
loadedProjectId.value = projectIdToLoad
|
||||
return
|
||||
}
|
||||
|
||||
@@ -122,21 +119,29 @@ watch(
|
||||
loadedProjectId.value = 0
|
||||
const projectFromStore = projectStore.projects[projectData.id]
|
||||
if (projectFromStore) {
|
||||
baseStore.handleSetCurrentProject({project: projectFromStore})
|
||||
baseStore.handleSetCurrentProject({project: projectFromStore, currentProjectViewId: props.viewId})
|
||||
}
|
||||
|
||||
// We create an extra project object instead of creating it in project.value because that would trigger a ui update which would result in bad ux.
|
||||
const project = new ProjectModel(projectData)
|
||||
try {
|
||||
const loadedProject = await projectService.value.get(project)
|
||||
baseStore.handleSetCurrentProject({project: loadedProject})
|
||||
baseStore.handleSetCurrentProject({project: loadedProject, currentProjectViewId: props.viewId})
|
||||
} finally {
|
||||
loadedProjectId.value = projectId
|
||||
loadedProjectId.value = projectIdToLoad
|
||||
}
|
||||
},
|
||||
{immediate: true},
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.viewId,
|
||||
() => {
|
||||
baseStore.setCurrentProjectViewId(props.viewId)
|
||||
},
|
||||
{immediate: true},
|
||||
)
|
||||
|
||||
function getViewTitle(view: IProjectView) {
|
||||
switch (view.title) {
|
||||
case 'List':
|
||||
|
||||
@@ -23,17 +23,16 @@ import {
|
||||
import {useDebounceFn} from '@vueuse/core'
|
||||
import {createRandomID} from '@/helpers/randomId'
|
||||
|
||||
const {
|
||||
modelValue,
|
||||
projectId,
|
||||
inputLabel = undefined,
|
||||
} = defineProps<{
|
||||
const props = defineProps<{
|
||||
modelValue: string,
|
||||
projectId?: number,
|
||||
inputLabel?: string,
|
||||
}>()
|
||||
|
||||
const emit = defineEmits(['update:modelValue', 'blur'])
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [value: string],
|
||||
'blur': [],
|
||||
}>()
|
||||
|
||||
const userService = new UserService()
|
||||
const projectUserService = new ProjectUserService()
|
||||
@@ -49,9 +48,9 @@ const {
|
||||
const id = ref(createRandomID())
|
||||
|
||||
watch(
|
||||
() => modelValue,
|
||||
() => props.modelValue,
|
||||
() => {
|
||||
filterQuery.value = modelValue
|
||||
filterQuery.value = props.modelValue
|
||||
},
|
||||
{immediate: true},
|
||||
)
|
||||
@@ -59,7 +58,7 @@ watch(
|
||||
watch(
|
||||
() => filterQuery.value,
|
||||
() => {
|
||||
if (filterQuery.value !== modelValue) {
|
||||
if (filterQuery.value !== props.modelValue) {
|
||||
emit('update:modelValue', filterQuery.value)
|
||||
}
|
||||
},
|
||||
@@ -204,6 +203,7 @@ const autocompleteResultType = ref<'labels' | 'assignees' | 'projects' | null>(n
|
||||
const autocompleteResults = ref<any[]>([])
|
||||
|
||||
function handleFieldInput() {
|
||||
if (!filterInput.value) return
|
||||
const cursorPosition = filterInput.value.selectionStart
|
||||
const textUpToCursor = filterQuery.value.substring(0, cursorPosition)
|
||||
autocompleteResults.value = []
|
||||
@@ -230,15 +230,15 @@ function handleFieldInput() {
|
||||
}
|
||||
if (matched.startsWith('assignee')) {
|
||||
autocompleteResultType.value = 'assignees'
|
||||
if (projectId) {
|
||||
projectUserService.getAll({projectId}, {s: search})
|
||||
if (props.projectId) {
|
||||
projectUserService.getAll({projectId: props.projectId}, {s: search})
|
||||
.then(users => autocompleteResults.value = users.length > 1 ? users : [])
|
||||
} else {
|
||||
userService.getAll({}, {s: search})
|
||||
.then(users => autocompleteResults.value = users.length > 1 ? users : [])
|
||||
}
|
||||
}
|
||||
if (!projectId && matched.startsWith('project')) {
|
||||
if (!props.projectId && matched.startsWith('project')) {
|
||||
autocompleteResultType.value = 'projects'
|
||||
autocompleteResults.value = projectStore.searchProject(search)
|
||||
}
|
||||
@@ -304,8 +304,7 @@ const blurDebounced = useDebounceFn(() => emit('blur'), 500)
|
||||
/>
|
||||
<DatepickerWithValues
|
||||
v-model="currentDatepickerValue"
|
||||
:open="datePickerPopupOpen"
|
||||
@close="() => datePickerPopupOpen = false"
|
||||
v-model:open="datePickerPopupOpen"
|
||||
@update:modelValue="updateDateInQuery"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
v-model="value"
|
||||
:has-title="true"
|
||||
class="filter-popup"
|
||||
@update:modelValue="emitChanges"
|
||||
@showResultsButtonClicked="() => modalOpen = false"
|
||||
:change-immediately="false"
|
||||
@showResults="showResults"
|
||||
/>
|
||||
</Modal>
|
||||
</template>
|
||||
@@ -32,8 +32,12 @@ import Filters from '@/components/project/partials/Filters.vue'
|
||||
|
||||
import {type TaskFilterParams} from '@/services/taskCollection'
|
||||
|
||||
const props = defineProps(['modelValue'])
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const props = defineProps<{
|
||||
modelValue: TaskFilterParams,
|
||||
}>()
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [value: TaskFilterParams]
|
||||
}>()
|
||||
|
||||
const value = ref<TaskFilterParams>({})
|
||||
|
||||
@@ -42,23 +46,27 @@ watch(
|
||||
(modelValue: TaskFilterParams) => {
|
||||
value.value = modelValue
|
||||
},
|
||||
{immediate: true},
|
||||
{
|
||||
immediate: true,
|
||||
deep: true,
|
||||
},
|
||||
)
|
||||
|
||||
function emitChanges(newValue: TaskFilterParams) {
|
||||
emit('update:modelValue', {
|
||||
...value.value,
|
||||
filter: newValue.filter,
|
||||
s: newValue.s,
|
||||
})
|
||||
}
|
||||
|
||||
const hasFilters = computed(() => {
|
||||
return value.value.filter !== '' ||
|
||||
value.value.s !== ''
|
||||
})
|
||||
|
||||
const modalOpen = ref(false)
|
||||
|
||||
function showResults() {
|
||||
emit('update:modelValue', {
|
||||
...value.value,
|
||||
filter: value.value.filter,
|
||||
s: value.value.s,
|
||||
})
|
||||
modalOpen.value = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -7,13 +7,14 @@
|
||||
<FilterInput
|
||||
v-model="filterQuery"
|
||||
:project-id="projectId"
|
||||
@blur="change()"
|
||||
@update:modelValue="() => change('modelValue')"
|
||||
@blur="() => change('blur')"
|
||||
/>
|
||||
|
||||
<div class="field is-flex is-flex-direction-column">
|
||||
<FancyCheckbox
|
||||
v-model="params.filter_include_nulls"
|
||||
@blur="change()"
|
||||
@change="() => change('always')"
|
||||
>
|
||||
{{ $t('filters.attributes.includeNulls') }}
|
||||
</FancyCheckbox>
|
||||
@@ -58,17 +59,21 @@ import {useProjectStore} from '@/stores/projects'
|
||||
import {FILTER_OPERATORS, transformFilterStringForApi, transformFilterStringFromApi} from '@/helpers/filters'
|
||||
import FilterInputDocs from '@/components/project/partials/FilterInputDocs.vue'
|
||||
|
||||
const {
|
||||
hasTitle = false,
|
||||
hasFooter = true,
|
||||
modelValue,
|
||||
} = defineProps<{
|
||||
const props = withDefaults(defineProps<{
|
||||
modelValue: TaskFilterParams,
|
||||
hasTitle?: boolean,
|
||||
hasFooter?: boolean,
|
||||
modelValue: TaskFilterParams,
|
||||
}>()
|
||||
changeImmediately?: boolean,
|
||||
}>(), {
|
||||
hasTitle: false,
|
||||
hasFooter: true,
|
||||
changeImmediately: false,
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue', 'showResultsButtonClicked'])
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [value: TaskFilterParams],
|
||||
'showResults': [],
|
||||
}>()
|
||||
|
||||
const route = useRoute()
|
||||
const projectId = computed(() => {
|
||||
@@ -102,7 +107,7 @@ const projectStore = useProjectStore()
|
||||
|
||||
// Using watchDebounced to prevent the filter re-triggering itself.
|
||||
watch(
|
||||
() => modelValue,
|
||||
() => props.modelValue,
|
||||
(value: TaskFilterParams) => {
|
||||
const val = {...value}
|
||||
val.filter = transformFilterStringFromApi(
|
||||
@@ -112,10 +117,28 @@ watch(
|
||||
)
|
||||
params.value = val
|
||||
},
|
||||
{immediate: true},
|
||||
{
|
||||
immediate: true,
|
||||
deep: true,
|
||||
},
|
||||
)
|
||||
|
||||
function change() {
|
||||
function change(event: 'blur' | 'modelValue' | 'always') {
|
||||
if (event !== 'always') {
|
||||
// The filter edit setting needs to save immediately, but the filter query edit in project views should
|
||||
// only change on blur, or it will show the filter replaced for api when the query is not yet complete.
|
||||
// This is highly confusing UX, hence we want to avoid that.
|
||||
// The approach taken here allows us to either toggle on blur or immediately, depending on the prop
|
||||
// value provided. This probably is a hacky way to do this, but it is also the most effective.
|
||||
if (props.changeImmediately && event === 'blur') {
|
||||
return
|
||||
}
|
||||
|
||||
if (!props.changeImmediately && event === 'modelValue') {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
const filter = transformFilterStringForApi(
|
||||
filterQuery.value,
|
||||
labelTitle => labelStore.getLabelByExactTitle(labelTitle)?.id || null,
|
||||
@@ -139,7 +162,7 @@ function change() {
|
||||
s,
|
||||
}
|
||||
|
||||
if (JSON.stringify(modelValue) === JSON.stringify(newParams)) {
|
||||
if (JSON.stringify(props.modelValue) === JSON.stringify(newParams)) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -148,7 +171,7 @@ function change() {
|
||||
|
||||
function changeAndEmitButton() {
|
||||
change()
|
||||
emit('showResultsButtonClicked')
|
||||
emit('showResults')
|
||||
}
|
||||
|
||||
function clearFiltersAndEmit() {
|
||||
|
||||
@@ -11,24 +11,19 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {computed, type PropType} from 'vue'
|
||||
import {computed} from 'vue'
|
||||
import type {IProject} from '@/modelTypes/IProject'
|
||||
|
||||
import ProjectCard from './ProjectCard.vue'
|
||||
|
||||
const props = defineProps({
|
||||
projects: {
|
||||
type: Array as PropType<IProject[]>,
|
||||
default: () => [],
|
||||
},
|
||||
showArchived: {
|
||||
default: false,
|
||||
type: Boolean,
|
||||
},
|
||||
itemLimit: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
const props = withDefaults(defineProps<{
|
||||
projects: IProject[],
|
||||
showArchived?: boolean,
|
||||
itemLimit?: boolean
|
||||
}>(), {
|
||||
projects: () => [],
|
||||
showArchived: false,
|
||||
itemLimit: false,
|
||||
})
|
||||
|
||||
const filteredProjects = computed(() => {
|
||||
|
||||
@@ -71,41 +71,40 @@
|
||||
trigger-icon="ellipsis-v"
|
||||
@close="() => showSetLimitInput = false"
|
||||
>
|
||||
<div
|
||||
v-if="showSetLimitInput"
|
||||
class="field has-addons"
|
||||
>
|
||||
<div class="control">
|
||||
<input
|
||||
ref="bucketLimitInputRef"
|
||||
v-focus.always
|
||||
:value="bucket.limit"
|
||||
class="input"
|
||||
type="number"
|
||||
min="0"
|
||||
@keyup.esc="() => showSetLimitInput = false"
|
||||
@keyup.enter="() => {setBucketLimit(bucket.id, true); showSetLimitInput = false}"
|
||||
@input="setBucketLimit(bucket.id)"
|
||||
>
|
||||
</div>
|
||||
<div class="control">
|
||||
<x-button
|
||||
v-cy="'setBucketLimit'"
|
||||
:disabled="bucket.limit < 0"
|
||||
:icon="['far', 'save']"
|
||||
:shadow="false"
|
||||
@click="() => {setBucketLimit(bucket.id, true); showSetLimitInput = false}"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<DropdownItem
|
||||
v-else
|
||||
@click.stop="showSetLimitInput = true"
|
||||
>
|
||||
<div
|
||||
v-if="showSetLimitInput"
|
||||
class="field has-addons"
|
||||
>
|
||||
<div class="control">
|
||||
<input
|
||||
ref="bucketLimitInputRef"
|
||||
v-focus.always
|
||||
:value="bucket.limit"
|
||||
class="input"
|
||||
type="number"
|
||||
min="0"
|
||||
@keyup.esc="() => showSetLimitInput = false"
|
||||
@keyup.enter="() => {setBucketLimit(bucket.id, true); showSetLimitInput = false}"
|
||||
@input="setBucketLimit(bucket.id)"
|
||||
>
|
||||
</div>
|
||||
<div class="control">
|
||||
<x-button
|
||||
v-cy="'setBucketLimit'"
|
||||
:disabled="bucket.limit < 0"
|
||||
:icon="['far', 'save']"
|
||||
:shadow="false"
|
||||
@click="setBucketLimit(bucket.id, true)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<template v-else>
|
||||
{{
|
||||
$t('project.kanban.limit', {limit: bucket.limit > 0 ? bucket.limit : $t('project.kanban.noLimit')})
|
||||
}}
|
||||
</template>
|
||||
{{
|
||||
$t('project.kanban.limit', {limit: bucket.limit > 0 ? bucket.limit : $t('project.kanban.noLimit')})
|
||||
}}
|
||||
</DropdownItem>
|
||||
<DropdownItem
|
||||
v-tooltip="$t('project.kanban.doneBucketHintExtended')"
|
||||
@@ -131,6 +130,7 @@
|
||||
</DropdownItem>
|
||||
<DropdownItem
|
||||
v-tooltip="buckets.length <= 1 ? $t('project.kanban.deleteLast') : ''"
|
||||
class="has-text-danger"
|
||||
:class="{'is-disabled': buckets.length <= 1}"
|
||||
icon-class="has-text-danger"
|
||||
icon="trash-alt"
|
||||
@@ -156,7 +156,7 @@
|
||||
>
|
||||
<template #footer>
|
||||
<div
|
||||
v-if="canWrite"
|
||||
v-if="canCreateTasks"
|
||||
class="bucket-footer"
|
||||
>
|
||||
<div
|
||||
@@ -309,10 +309,7 @@ import ProjectViewModel from '@/models/projectView'
|
||||
import TaskBucketService from '@/services/taskBucket'
|
||||
import TaskBucketModel from '@/models/taskBucket'
|
||||
|
||||
const {
|
||||
projectId,
|
||||
viewId,
|
||||
} = defineProps<{
|
||||
const props = defineProps<{
|
||||
projectId: number,
|
||||
viewId: IProjectView['id'],
|
||||
}>()
|
||||
@@ -392,9 +389,10 @@ const bucketDraggableComponentData = computed(() => ({
|
||||
{'dragging-disabled': !canWrite.value},
|
||||
],
|
||||
}))
|
||||
const project = computed(() => projectId ? projectStore.projects[projectId] : null)
|
||||
const view = computed<IProjectView | null>(() => project.value?.views.find(v => v.id === viewId) || null)
|
||||
const project = computed(() => props.projectId ? projectStore.projects[props.projectId] : null)
|
||||
const view = computed(() => project.value?.views.find(v => v.id === props.viewId) as IProjectView || null)
|
||||
const canWrite = computed(() => baseStore.currentProject?.maxRight > Rights.READ && view.value.bucketConfigurationMode === 'manual')
|
||||
const canCreateTasks = computed(() => canWrite.value && props.projectId > 0)
|
||||
const buckets = computed(() => kanbanStore.buckets)
|
||||
const loading = computed(() => kanbanStore.isLoading)
|
||||
|
||||
@@ -403,10 +401,10 @@ const taskLoading = computed(() => taskStore.isLoading || taskPositionService.va
|
||||
watch(
|
||||
() => ({
|
||||
params: params.value,
|
||||
projectId,
|
||||
viewId,
|
||||
projectId: props.projectId,
|
||||
viewId: props.viewId,
|
||||
}),
|
||||
({params}) => {
|
||||
({params, projectId, viewId}) => {
|
||||
if (projectId === undefined || Number(projectId) === 0) {
|
||||
return
|
||||
}
|
||||
@@ -435,8 +433,8 @@ function handleTaskContainerScroll(id: IBucket['id'], el: HTMLElement) {
|
||||
}
|
||||
|
||||
kanbanStore.loadNextTasksForBucket(
|
||||
projectId,
|
||||
viewId,
|
||||
props.projectId,
|
||||
props.viewId,
|
||||
params.value,
|
||||
id,
|
||||
)
|
||||
@@ -490,12 +488,6 @@ async function updateTaskPosition(e) {
|
||||
taskBefore !== null ? taskBefore.position : null,
|
||||
taskAfter !== null ? taskAfter.position : null,
|
||||
)
|
||||
if (
|
||||
oldBucket !== undefined && // This shouldn't actually be `undefined`, but let's play it safe.
|
||||
newBucket.id !== oldBucket.id
|
||||
) {
|
||||
newTask.done = project.value?.doneBucketId === newBucket.id
|
||||
}
|
||||
|
||||
let bucketHasChanged = false
|
||||
if (
|
||||
@@ -516,18 +508,23 @@ async function updateTaskPosition(e) {
|
||||
try {
|
||||
const newPosition = new TaskPositionModel({
|
||||
position,
|
||||
projectViewId: viewId,
|
||||
projectViewId: props.viewId,
|
||||
taskId: newTask.id,
|
||||
})
|
||||
await taskPositionService.value.update(newPosition)
|
||||
|
||||
if(bucketHasChanged) {
|
||||
await taskBucketService.value.update(new TaskBucketModel({
|
||||
const updatedTaskBucket = await taskBucketService.value.update(new TaskBucketModel({
|
||||
taskId: newTask.id,
|
||||
bucketId: newTask.bucketId,
|
||||
projectViewId: viewId,
|
||||
projectViewId: props.viewId,
|
||||
projectId: project.value.id,
|
||||
}))
|
||||
newTask.done = updatedTaskBucket.taskDone
|
||||
if (updatedTaskBucket.bucketId !== newTask.bucketId) {
|
||||
kanbanStore.moveTaskToBucket(newTask, updatedTaskBucket.bucketId)
|
||||
}
|
||||
kanbanStore.setTaskInBucket(newTask)
|
||||
}
|
||||
|
||||
// Make sure the first and second task don't both get position 0 assigned
|
||||
@@ -586,7 +583,7 @@ async function createNewBucket() {
|
||||
await kanbanStore.createBucket(new BucketModel({
|
||||
title: newBucketTitle.value,
|
||||
projectId: project.value.id,
|
||||
projectViewId: viewId,
|
||||
projectViewId: props.viewId,
|
||||
}))
|
||||
newBucketTitle.value = ''
|
||||
}
|
||||
@@ -606,7 +603,7 @@ async function deleteBucket() {
|
||||
bucket: new BucketModel({
|
||||
id: bucketToDelete.value,
|
||||
projectId: project.value.id,
|
||||
projectViewId: viewId,
|
||||
projectViewId: props.viewId,
|
||||
}),
|
||||
params: params.value,
|
||||
})
|
||||
@@ -635,7 +632,7 @@ async function saveBucketTitle(bucketId: IBucket['id'], bucketTitle: string) {
|
||||
await kanbanStore.updateBucket({
|
||||
id: bucketId,
|
||||
title: bucketTitle,
|
||||
projectId,
|
||||
projectId: props.projectId,
|
||||
})
|
||||
success({message: i18n.global.t('project.kanban.bucketTitleSavedSuccess')})
|
||||
bucketTitleEditable.value = false
|
||||
@@ -657,7 +654,7 @@ function updateBucketPosition(e: { newIndex: number }) {
|
||||
|
||||
kanbanStore.updateBucket({
|
||||
id: bucket.id,
|
||||
projectId,
|
||||
projectId: props.projectId,
|
||||
position: calculateItemPosition(
|
||||
bucketBefore !== null ? bucketBefore.position : null,
|
||||
bucketAfter !== null ? bucketAfter.position : null,
|
||||
@@ -672,7 +669,7 @@ async function saveBucketLimit(bucketId: IBucket['id'], limit: number) {
|
||||
|
||||
await kanbanStore.updateBucket({
|
||||
...kanbanStore.getBucketById(bucketId),
|
||||
projectId,
|
||||
projectId: props.projectId,
|
||||
limit,
|
||||
})
|
||||
success({message: t('project.kanban.bucketLimitSavedSuccess')})
|
||||
|
||||
@@ -120,10 +120,7 @@ import type {IProjectView} from '@/modelTypes/IProjectView'
|
||||
import TaskPositionService from '@/services/taskPosition'
|
||||
import TaskPositionModel from '@/models/taskPosition'
|
||||
|
||||
const {
|
||||
projectId,
|
||||
viewId,
|
||||
} = defineProps<{
|
||||
const props = defineProps<{
|
||||
projectId: IProject['id'],
|
||||
viewId: IProjectView['id'],
|
||||
}>()
|
||||
@@ -144,7 +141,7 @@ const {
|
||||
loadTasks,
|
||||
params,
|
||||
sortByParam,
|
||||
} = useTaskList(() => projectId, () => viewId, {position: 'asc'})
|
||||
} = useTaskList(() => props.projectId, () => props.viewId, {position: 'asc'})
|
||||
|
||||
const taskPositionService = ref(new TaskPositionService())
|
||||
|
||||
@@ -153,7 +150,7 @@ watch(
|
||||
allTasks,
|
||||
() => {
|
||||
tasks.value = [...allTasks.value]
|
||||
if (projectId < 0) {
|
||||
if (props.projectId < 0) {
|
||||
return
|
||||
}
|
||||
const tasksById = {}
|
||||
@@ -223,6 +220,13 @@ function updateTaskList(task: ITask) {
|
||||
}
|
||||
|
||||
function updateTasks(updatedTask: ITask) {
|
||||
if (props.projectId < 0) {
|
||||
// In the case of a filter, we'll reload the filter in the background to avoid tasks which do
|
||||
// not match the filter show up here
|
||||
loadTasks(false)
|
||||
return
|
||||
}
|
||||
|
||||
for (const t in tasks.value) {
|
||||
if (tasks.value[t].id === updatedTask.id) {
|
||||
tasks.value[t] = updatedTask
|
||||
@@ -242,7 +246,7 @@ async function saveTaskPosition(e) {
|
||||
|
||||
await taskPositionService.value.update(new TaskPositionModel({
|
||||
position,
|
||||
projectViewId: viewId,
|
||||
projectViewId: props.viewId,
|
||||
taskId: task.id,
|
||||
}))
|
||||
tasks.value[e.newIndex] = {
|
||||
@@ -311,7 +315,7 @@ function prepareFiltersAndLoadTasks() {
|
||||
|
||||
.list-view {
|
||||
padding-bottom: 1rem;
|
||||
|
||||
|
||||
:deep(.card) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
@@ -290,10 +290,7 @@ import type {IProject} from '@/modelTypes/IProject'
|
||||
import AssigneeList from '@/components/tasks/partials/AssigneeList.vue'
|
||||
import type {IProjectView} from '@/modelTypes/IProjectView'
|
||||
|
||||
const {
|
||||
projectId,
|
||||
viewId,
|
||||
} = defineProps<{
|
||||
const props = defineProps<{
|
||||
projectId: IProject['id'],
|
||||
viewId: IProjectView['id'],
|
||||
}>()
|
||||
@@ -322,7 +319,7 @@ const SORT_BY_DEFAULT: SortBy = {
|
||||
const activeColumns = useStorage('tableViewColumns', {...ACTIVE_COLUMNS_DEFAULT})
|
||||
const sortBy = useStorage<SortBy>('tableViewSortBy', {...SORT_BY_DEFAULT})
|
||||
|
||||
const taskList = useTaskList(() => projectId, () => viewId, sortBy.value)
|
||||
const taskList = useTaskList(() => props.projectId, () => props.viewId, sortBy.value)
|
||||
|
||||
const {
|
||||
loading,
|
||||
|
||||
@@ -8,17 +8,19 @@ import {useLabelStore} from '@/stores/labels'
|
||||
import {useProjectStore} from '@/stores/projects'
|
||||
import FilterInputDocs from '@/components/project/partials/FilterInputDocs.vue'
|
||||
|
||||
const {
|
||||
modelValue,
|
||||
loading = false,
|
||||
showSaveButtons = false,
|
||||
} = defineProps<{
|
||||
const props = withDefaults(defineProps<{
|
||||
modelValue: IProjectView,
|
||||
loading?: bool,
|
||||
showSaveButtons?: bool,
|
||||
}>()
|
||||
loading?: boolean,
|
||||
showSaveButtons?: boolean,
|
||||
}>(), {
|
||||
loading: false,
|
||||
showSaveButtons: false,
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue', 'cancel'])
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [value: IProjectView],
|
||||
'cancel': [],
|
||||
}>()
|
||||
|
||||
const view = ref<IProjectView>()
|
||||
|
||||
@@ -26,16 +28,16 @@ const labelStore = useLabelStore()
|
||||
const projectStore = useProjectStore()
|
||||
|
||||
onBeforeMount(() => {
|
||||
const transform = filterString => transformFilterStringFromApi(
|
||||
const transform = (filterString: string) => transformFilterStringFromApi(
|
||||
filterString,
|
||||
labelId => labelStore.getLabelById(labelId)?.title,
|
||||
projectId => projectStore.projects[projectId]?.title || null,
|
||||
)
|
||||
|
||||
const transformed = {
|
||||
...modelValue,
|
||||
filter: transform(modelValue.filter),
|
||||
bucketConfiguration: modelValue.bucketConfiguration.map(bc => ({
|
||||
...props.modelValue,
|
||||
filter: transform(props.modelValue.filter),
|
||||
bucketConfiguration: props.modelValue.bucketConfiguration.map(bc => ({
|
||||
title: bc.title,
|
||||
filter: transform(bc.filter),
|
||||
})),
|
||||
@@ -47,7 +49,7 @@ onBeforeMount(() => {
|
||||
})
|
||||
|
||||
function save() {
|
||||
const transformFilter = filterQuery => transformFilterStringForApi(
|
||||
const transformFilter = (filterQuery: string) => transformFilterStringForApi(
|
||||
filterQuery,
|
||||
labelTitle => labelStore.filterLabelsByQuery([], labelTitle)[0]?.id || null,
|
||||
projectTitle => {
|
||||
@@ -73,7 +75,7 @@ function validateTitle() {
|
||||
}
|
||||
|
||||
function handleBubbleSave() {
|
||||
if (showSaveButtons) {
|
||||
if (props.showSaveButtons) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -162,7 +162,6 @@ export default {name: 'UserTeamShare'}
|
||||
|
||||
<script setup lang="ts">
|
||||
import {ref, reactive, computed, shallowReactive, type Ref} from 'vue'
|
||||
import type {PropType} from 'vue'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
|
||||
import UserProjectService from '@/services/userProject'
|
||||
@@ -192,23 +191,16 @@ import User from '@/components/misc/User.vue'
|
||||
|
||||
// FIXME: I think this whole thing can now only manage user/team sharing for projects? Maybe remove a little generalization?
|
||||
|
||||
const props = defineProps({
|
||||
type: {
|
||||
type: String as PropType<'project'>,
|
||||
default: '',
|
||||
},
|
||||
shareType: {
|
||||
type: String as PropType<'user' | 'team'>,
|
||||
default: '',
|
||||
},
|
||||
id: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
userIsAdmin: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
const props = withDefaults(defineProps<{
|
||||
type: 'project',
|
||||
shareType: 'user' | 'team',
|
||||
id: number,
|
||||
userIsAdmin?: boolean
|
||||
}>(), {
|
||||
type: '',
|
||||
shareType: '',
|
||||
id: 0,
|
||||
userIsAdmin: false,
|
||||
})
|
||||
|
||||
const {t} = useI18n({useScope: 'global'})
|
||||
|
||||
@@ -194,15 +194,17 @@ import {useTaskStore} from '@/stores/tasks'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
import FilePreview from '@/components/tasks/partials/FilePreview.vue'
|
||||
|
||||
const {
|
||||
task,
|
||||
editEnabled = true,
|
||||
} = defineProps<{
|
||||
const props = withDefaults(defineProps<{
|
||||
task: ITask,
|
||||
editEnabled: boolean,
|
||||
}>()
|
||||
}>(), {
|
||||
editEnabled: true,
|
||||
})
|
||||
|
||||
// FIXME: this should go through the store
|
||||
const emit = defineEmits(['taskChanged'])
|
||||
const emit = defineEmits<{
|
||||
'taskChanged': [ITask],
|
||||
}>()
|
||||
const taskStore = useTaskStore()
|
||||
const {t} = useI18n({useScope: 'global'})
|
||||
|
||||
@@ -238,7 +240,7 @@ function uploadNewAttachment() {
|
||||
}
|
||||
|
||||
function uploadFilesToTask(files: File[] | FileList) {
|
||||
uploadFiles(attachmentService, task.id, files)
|
||||
uploadFiles(attachmentService, props.task.id, files)
|
||||
}
|
||||
|
||||
const attachmentToDelete = ref<IAttachment | null>(null)
|
||||
@@ -279,11 +281,11 @@ function canPreview(attachment: IAttachment): boolean {
|
||||
const copy = useCopyToClipboard()
|
||||
|
||||
function copyUrl(attachment: IAttachment) {
|
||||
copy(generateAttachmentUrl(task.id, attachment.id))
|
||||
copy(generateAttachmentUrl(props.task.id, attachment.id))
|
||||
}
|
||||
|
||||
async function setCoverImage(attachment: IAttachment | null) {
|
||||
const updatedTask = await taskStore.setCoverImage(task, attachment)
|
||||
const updatedTask = await taskStore.setCoverImage(props.task, attachment)
|
||||
emit('taskChanged', updatedTask)
|
||||
success({message: t('task.attachment.successfullyChangedCoverImage')})
|
||||
}
|
||||
|
||||
@@ -200,15 +200,11 @@ import {useConfigStore} from '@/stores/config'
|
||||
import {useAuthStore} from '@/stores/auth'
|
||||
import Reactions from '@/components/input/Reactions.vue'
|
||||
|
||||
const props = defineProps({
|
||||
taskId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
canWrite: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
const props = withDefaults(defineProps<{
|
||||
taskId: number,
|
||||
canWrite?: boolean
|
||||
}>(), {
|
||||
canWrite: true,
|
||||
})
|
||||
|
||||
const {t} = useI18n({useScope: 'global'})
|
||||
|
||||
@@ -9,10 +9,9 @@
|
||||
<script setup lang="ts">
|
||||
import {formatISO, formatDateLong, formatDateSince} from '@/helpers/time/formatDate'
|
||||
|
||||
defineProps({
|
||||
date: {
|
||||
type: Date,
|
||||
default: 0,
|
||||
},
|
||||
withDefaults(defineProps<{
|
||||
date?: Date
|
||||
}>(), {
|
||||
date: 0,
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -46,13 +46,13 @@ import TaskService from '@/services/task'
|
||||
import type {ITask} from '@/modelTypes/ITask'
|
||||
import { getFlatpickrLanguage } from '@/helpers/flatpickrLanguage'
|
||||
|
||||
const {
|
||||
modelValue,
|
||||
} = defineProps<{
|
||||
const props = defineProps<{
|
||||
modelValue: ITask,
|
||||
}>()
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [value: ITask]
|
||||
}>()
|
||||
|
||||
const {t} = useI18n({useScope: 'global'})
|
||||
|
||||
@@ -65,7 +65,7 @@ const lastValue = ref<Date | null>()
|
||||
const changeInterval = ref<ReturnType<typeof setInterval>>()
|
||||
|
||||
watch(
|
||||
() => modelValue,
|
||||
() => props.modelValue,
|
||||
(value) => {
|
||||
task.value = { ...value }
|
||||
dueDate.value = value.dueDate
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {ref, shallowReactive, watch, nextTick, type PropType} from 'vue'
|
||||
import {ref, shallowReactive, watch, nextTick} from 'vue'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
|
||||
import User from '@/components/misc/User.vue'
|
||||
@@ -46,25 +46,19 @@ import type {IUser} from '@/modelTypes/IUser'
|
||||
import {getDisplayName} from '@/models/user'
|
||||
import AssigneeList from '@/components/tasks/partials/AssigneeList.vue'
|
||||
|
||||
const props = defineProps({
|
||||
taskId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
projectId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
modelValue: {
|
||||
type: Array as PropType<IUser[]>,
|
||||
default: () => [],
|
||||
},
|
||||
const props = withDefaults(defineProps<{
|
||||
modelValue: IUser[] | undefined,
|
||||
taskId: number,
|
||||
projectId: number,
|
||||
disabled?: boolean,
|
||||
}>(), {
|
||||
modelValue: () => [],
|
||||
disabled: false,
|
||||
})
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [value: IUser[] | undefined],
|
||||
}>()
|
||||
|
||||
const taskStore = useTaskStore()
|
||||
const {t} = useI18n({useScope: 'global'})
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {type PropType, ref, computed, shallowReactive, watch} from 'vue'
|
||||
import {ref, computed, shallowReactive, watch} from 'vue'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
|
||||
import LabelModel from '@/models/label'
|
||||
@@ -60,27 +60,21 @@ import {useLabelStore} from '@/stores/labels'
|
||||
import {useTaskStore} from '@/stores/tasks'
|
||||
import {getRandomColorHex} from '@/helpers/color/randomColor'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Array as PropType<ILabel[]>,
|
||||
default: () => [],
|
||||
},
|
||||
taskId: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 0,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
creatable: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
const props = withDefaults(defineProps<{
|
||||
modelValue: ILabel[] | undefined
|
||||
taskId?: number
|
||||
disabled?: boolean
|
||||
creatable?: boolean
|
||||
}>(), {
|
||||
modelValue: () => [],
|
||||
taskId: 0,
|
||||
disabled: false,
|
||||
creatable: true,
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [labels: ILabel[]],
|
||||
}>()
|
||||
|
||||
const {t} = useI18n({useScope: 'global'})
|
||||
|
||||
|
||||
@@ -19,17 +19,14 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {type PropType, ref, shallowReactive, watchEffect} from 'vue'
|
||||
import {ref, shallowReactive, watchEffect} from 'vue'
|
||||
import AttachmentService from '@/services/attachment'
|
||||
import type {IAttachment} from '@/modelTypes/IAttachment'
|
||||
import {SUPPORTED_IMAGE_SUFFIX} from '@/models/attachment'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Object as PropType<IAttachment>,
|
||||
default: undefined,
|
||||
},
|
||||
})
|
||||
const props = defineProps<{
|
||||
modelValue?: IAttachment
|
||||
}>()
|
||||
|
||||
const attachmentService = shallowReactive(new AttachmentService())
|
||||
const blobUrl = ref<string | undefined>(undefined)
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {ref, computed, type PropType} from 'vue'
|
||||
import {ref, computed} from 'vue'
|
||||
import {useRouter} from 'vue-router'
|
||||
|
||||
import BaseButton from '@/components/base/BaseButton.vue'
|
||||
@@ -63,18 +63,16 @@ import {useTaskStore} from '@/stores/tasks'
|
||||
import type {ITask} from '@/modelTypes/ITask'
|
||||
import {getHexColor, getTaskIdentifier} from '@/models/task'
|
||||
|
||||
const props = defineProps({
|
||||
task: {
|
||||
type: Object as PropType<ITask>,
|
||||
required: true,
|
||||
},
|
||||
canWrite: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
const props = withDefaults(defineProps<{
|
||||
task: ITask,
|
||||
canWrite: boolean | undefined,
|
||||
}>(), {
|
||||
canWrite: false,
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:task'])
|
||||
const emit = defineEmits<{
|
||||
'update:task': [task: ITask]
|
||||
}>()
|
||||
|
||||
const router = useRouter()
|
||||
const copy = useCopyToClipboard()
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
v-if="coverImageBlobUrl"
|
||||
:src="coverImageBlobUrl"
|
||||
alt=""
|
||||
class="cover-image"
|
||||
class="tw-w-full"
|
||||
>
|
||||
<div class="p-2">
|
||||
<span class="task-id">
|
||||
@@ -111,23 +111,21 @@ import {formatDateLong, formatISO, formatDateSince} from '@/helpers/time/formatD
|
||||
import {colorIsDark} from '@/helpers/color/colorIsDark'
|
||||
import {useTaskStore} from '@/stores/tasks'
|
||||
import AssigneeList from '@/components/tasks/partials/AssigneeList.vue'
|
||||
import {useAuthStore} from '@/stores/auth'
|
||||
import {playPopSound} from '@/helpers/playPop'
|
||||
import {isEditorContentEmpty} from '@/helpers/editorContentEmpty'
|
||||
|
||||
const {
|
||||
task,
|
||||
loading = false,
|
||||
} = defineProps<{
|
||||
const props = withDefaults(defineProps<{
|
||||
task: ITask,
|
||||
loading: boolean,
|
||||
}>()
|
||||
}>(), {
|
||||
loading: false,
|
||||
})
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
const loadingInternal = ref(false)
|
||||
|
||||
const color = computed(() => getHexColor(task.hexColor))
|
||||
const color = computed(() => getHexColor(props.task.hexColor))
|
||||
|
||||
async function toggleTaskDone(task: ITask) {
|
||||
loadingInternal.value = true
|
||||
@@ -137,7 +135,7 @@ async function toggleTaskDone(task: ITask) {
|
||||
done: !task.done,
|
||||
})
|
||||
|
||||
if (updatedTask.done && useAuthStore().settings.frontendSettings.playSoundWhenDone) {
|
||||
if (updatedTask.done) {
|
||||
playPopSound()
|
||||
}
|
||||
} finally {
|
||||
@@ -148,7 +146,7 @@ async function toggleTaskDone(task: ITask) {
|
||||
function openTaskDetail() {
|
||||
router.push({
|
||||
name: 'task.detail',
|
||||
params: {id: task.id},
|
||||
params: {id: props.task.id},
|
||||
state: {backdropView: router.currentRoute.value.fullPath},
|
||||
})
|
||||
}
|
||||
@@ -156,12 +154,12 @@ function openTaskDetail() {
|
||||
const coverImageBlobUrl = ref<string | null>(null)
|
||||
|
||||
async function maybeDownloadCoverImage() {
|
||||
if (!task.coverImageAttachmentId) {
|
||||
if (!props.task.coverImageAttachmentId) {
|
||||
coverImageBlobUrl.value = null
|
||||
return
|
||||
}
|
||||
|
||||
const attachment = task.attachments.find(a => a.id === task.coverImageAttachmentId)
|
||||
const attachment = props.task.attachments.find(a => a.id === props.task.coverImageAttachmentId)
|
||||
if (!attachment || !SUPPORTED_IMAGE_SUFFIX.some((suffix) => attachment.file.name.toLowerCase().endsWith(suffix))) {
|
||||
return
|
||||
}
|
||||
@@ -171,7 +169,7 @@ async function maybeDownloadCoverImage() {
|
||||
}
|
||||
|
||||
watch(
|
||||
() => task.coverImageAttachmentId,
|
||||
() => props.task.coverImageAttachmentId,
|
||||
maybeDownloadCoverImage,
|
||||
{immediate: true},
|
||||
)
|
||||
|
||||
@@ -4,63 +4,25 @@
|
||||
v-model.number="percentDone"
|
||||
:disabled="disabled || undefined"
|
||||
>
|
||||
<option value="0">
|
||||
0%
|
||||
</option>
|
||||
<option value="0.1">
|
||||
10%
|
||||
</option>
|
||||
<option value="0.2">
|
||||
20%
|
||||
</option>
|
||||
<option value="0.3">
|
||||
30%
|
||||
</option>
|
||||
<option value="0.4">
|
||||
40%
|
||||
</option>
|
||||
<option value="0.5">
|
||||
50%
|
||||
</option>
|
||||
<option value="0.6">
|
||||
60%
|
||||
</option>
|
||||
<option value="0.7">
|
||||
70%
|
||||
</option>
|
||||
<option value="0.8">
|
||||
80%
|
||||
</option>
|
||||
<option value="0.9">
|
||||
90%
|
||||
</option>
|
||||
<option value="1">
|
||||
100%
|
||||
<option
|
||||
v-for="option in PERCENT_OPTIONS"
|
||||
:key="option"
|
||||
:value="option"
|
||||
>
|
||||
{{ option * 100 }}%
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {computed} from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
default: 0,
|
||||
type: Number,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
withDefaults(defineProps<{
|
||||
disabled?: boolean
|
||||
}>(), {
|
||||
disabled: false,
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const percentDone = defineModel<number>({ required: true })
|
||||
|
||||
const percentDone = computed({
|
||||
get: () => props.modelValue,
|
||||
set(percentDone) {
|
||||
emit('update:modelValue', percentDone)
|
||||
},
|
||||
})
|
||||
const PERCENT_OPTIONS = [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1] as const
|
||||
</script>
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
<template>
|
||||
<span
|
||||
v-if="!done && (showAll || priority >= priorities.HIGH)"
|
||||
:class="{'not-so-high': priority === priorities.HIGH, 'high-priority': priority >= priorities.HIGH}"
|
||||
:class="{
|
||||
'not-so-high': priority === priorities.HIGH,
|
||||
'high-priority': priority >= priorities.HIGH
|
||||
}"
|
||||
class="priority-label"
|
||||
>
|
||||
<span
|
||||
v-if="priority >= priorities.HIGH"
|
||||
class="icon"
|
||||
>
|
||||
<Icon icon="exclamation" />
|
||||
<Icon icon="exclamation-circle" />
|
||||
</span>
|
||||
<span>
|
||||
<template v-if="priority === priorities.UNSET">{{ $t('task.priority.unset') }}</template>
|
||||
@@ -18,47 +21,36 @@
|
||||
<template v-if="priority === priorities.URGENT">{{ $t('task.priority.urgent') }}</template>
|
||||
<template v-if="priority === priorities.DO_NOW">{{ $t('task.priority.doNow') }}</template>
|
||||
</span>
|
||||
<span
|
||||
v-if="priority === priorities.DO_NOW"
|
||||
class="icon pr-0"
|
||||
>
|
||||
<Icon icon="exclamation" />
|
||||
</span>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {PRIORITIES as priorities} from '@/constants/priorities'
|
||||
|
||||
defineProps({
|
||||
priority: {
|
||||
default: 0,
|
||||
type: Number,
|
||||
},
|
||||
showAll: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
done: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
withDefaults(defineProps<{
|
||||
priority: number,
|
||||
showAll?: boolean,
|
||||
done?: boolean
|
||||
}>(), {
|
||||
priority: priorities.UNSET,
|
||||
showAll: false,
|
||||
done: false,
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
span.high-priority {
|
||||
.high-priority {
|
||||
color: var(--danger);
|
||||
width: auto !important; // To override the width set in tasks
|
||||
}
|
||||
|
||||
.icon {
|
||||
vertical-align: top;
|
||||
width: auto !important;
|
||||
padding: 0 .5rem;
|
||||
}
|
||||
.not-so-high {
|
||||
color: var(--warning);
|
||||
}
|
||||
|
||||
&.not-so-high {
|
||||
color: var(--warning);
|
||||
}
|
||||
.icon {
|
||||
vertical-align: top;
|
||||
width: auto !important;
|
||||
padding-right: .5rem;
|
||||
}
|
||||
</style>
|
||||
@@ -3,7 +3,6 @@
|
||||
<select
|
||||
v-model="priority"
|
||||
:disabled="disabled || undefined"
|
||||
@change="updateData"
|
||||
>
|
||||
<option :value="PRIORITIES.UNSET">
|
||||
{{ $t('task.priority.unset') }}
|
||||
@@ -28,32 +27,17 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {ref, watch} from 'vue'
|
||||
import {PRIORITIES} from '@/constants/priorities'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
withDefaults(defineProps<{
|
||||
disabled?: boolean
|
||||
}>(), {
|
||||
disabled: false,
|
||||
})
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
const priority = ref(0)
|
||||
const priority = defineModel<number>({
|
||||
required: true,
|
||||
default: 0,
|
||||
})
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(value) => {
|
||||
priority.value = value
|
||||
},
|
||||
{immediate: true},
|
||||
)
|
||||
|
||||
function updateData() {
|
||||
emit('update:modelValue', priority.value)
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -31,19 +31,23 @@ import {useProjectStore} from '@/stores/projects'
|
||||
import {getProjectTitle} from '@/helpers/getProjectTitle'
|
||||
|
||||
import ProjectModel from '@/models/project'
|
||||
|
||||
import Multiselect from '@/components/input/Multiselect.vue'
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
modelValue: IProject | null
|
||||
modelValue?: IProject
|
||||
savedFiltersOnly?: boolean
|
||||
filter?: (project: IProject) => boolean,
|
||||
}>(), {
|
||||
modelValue: () => new ProjectModel(),
|
||||
savedFiltersOnly: false,
|
||||
filter: () => true,
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [value: IProject]
|
||||
}>()
|
||||
|
||||
const project: IProject = reactive(new ProjectModel())
|
||||
const project = reactive<IProject>(new ProjectModel())
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
@@ -55,6 +59,7 @@ watch(
|
||||
)
|
||||
|
||||
const projectStore = useProjectStore()
|
||||
|
||||
const foundProjects = ref<IProject[]>([])
|
||||
function findProjects(query: string) {
|
||||
if (query === '') {
|
||||
@@ -62,11 +67,13 @@ function findProjects(query: string) {
|
||||
}
|
||||
|
||||
if (props.savedFiltersOnly) {
|
||||
foundProjects.value = projectStore.searchSavedFilter(query)
|
||||
const found = projectStore.searchSavedFilter(query)
|
||||
foundProjects.value = found.filter(props.filter)
|
||||
return
|
||||
}
|
||||
|
||||
foundProjects.value = projectStore.searchProject(query)
|
||||
const found = projectStore.searchProject(query)
|
||||
foundProjects.value = found.filter(props.filter)
|
||||
}
|
||||
|
||||
function select(p: IProject | null) {
|
||||
|
||||
@@ -204,7 +204,6 @@ import FancyCheckbox from '@/components/input/FancyCheckbox.vue'
|
||||
import {error, success} from '@/message'
|
||||
import {useTaskStore} from '@/stores/tasks'
|
||||
import {useProjectStore} from '@/stores/projects'
|
||||
import {useAuthStore} from '@/stores/auth'
|
||||
import {playPopSound} from '@/helpers/playPop'
|
||||
|
||||
const props = defineProps({
|
||||
@@ -380,7 +379,7 @@ async function createAndRelateTask(title: string) {
|
||||
async function toggleTaskDone(task: ITask) {
|
||||
await taskStore.update(task)
|
||||
|
||||
if (task.done && useAuthStore().settings.frontendSettings.playSoundWhenDone) {
|
||||
if (task.done) {
|
||||
playPopSound()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<Popup @close="showFormSwitch = null">
|
||||
<Popup @update:open="showFormSwitch = null">
|
||||
<template #trigger="{toggle}">
|
||||
<SimpleButton
|
||||
v-tooltip="reminder.reminder && reminder.relativeTo !== null ? formatDateShort(reminder.reminder) : null"
|
||||
@@ -50,7 +50,7 @@
|
||||
/>
|
||||
|
||||
<DatepickerInline
|
||||
v-if="activeForm === 'absolute'"
|
||||
v-else-if="activeForm === 'absolute'"
|
||||
v-model="reminderDate"
|
||||
@update:modelValue="setReminderDateAndClose(close)"
|
||||
/>
|
||||
@@ -88,38 +88,38 @@ import Card from '@/components/misc/Card.vue'
|
||||
import SimpleButton from '@/components/input/SimpleButton.vue'
|
||||
import {useDebounceFn} from '@vueuse/core'
|
||||
|
||||
const {
|
||||
modelValue,
|
||||
clearAfterUpdate = false,
|
||||
defaultRelativeTo = REMINDER_PERIOD_RELATIVE_TO_TYPES.DUEDATE,
|
||||
} = defineProps<{
|
||||
modelValue?: ITaskReminder,
|
||||
const props = withDefaults(defineProps<{
|
||||
modelValue: ITaskReminder | undefined,
|
||||
clearAfterUpdate?: boolean,
|
||||
defaultRelativeTo?: null | IReminderPeriodRelativeTo,
|
||||
}>()
|
||||
defaultRelativeTo?: IReminderPeriodRelativeTo | null,
|
||||
}>(), {
|
||||
modelValue: new TaskReminderModel() as ITaskReminder,
|
||||
clearAfterUpdate: false,
|
||||
defaultRelativeTo: REMINDER_PERIOD_RELATIVE_TO_TYPES.DUEDATE,
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [value: ITaskReminder | undefined],
|
||||
}>()
|
||||
|
||||
const {t} = useI18n({useScope: 'global'})
|
||||
|
||||
const reminder = ref<ITaskReminder>(new TaskReminderModel())
|
||||
|
||||
const presets = computed<TaskReminderModel[]>(() => [
|
||||
{reminder: null, relativePeriod: 0, relativeTo: defaultRelativeTo},
|
||||
{reminder: null, relativePeriod: -2 * SECONDS_A_HOUR, relativeTo: defaultRelativeTo},
|
||||
{reminder: null, relativePeriod: -1 * SECONDS_A_DAY, relativeTo: defaultRelativeTo},
|
||||
{reminder: null, relativePeriod: -1 * SECONDS_A_DAY * 3, relativeTo: defaultRelativeTo},
|
||||
{reminder: null, relativePeriod: -1 * SECONDS_A_DAY * 7, relativeTo: defaultRelativeTo},
|
||||
{reminder: null, relativePeriod: -1 * SECONDS_A_DAY * 30, relativeTo: defaultRelativeTo},
|
||||
])
|
||||
const presets = computed(() => [
|
||||
{reminder: null, relativePeriod: 0, relativeTo: props.defaultRelativeTo},
|
||||
{reminder: null, relativePeriod: -2 * SECONDS_A_HOUR, relativeTo: props.defaultRelativeTo},
|
||||
{reminder: null, relativePeriod: -1 * SECONDS_A_DAY, relativeTo: props.defaultRelativeTo},
|
||||
{reminder: null, relativePeriod: -1 * SECONDS_A_DAY * 3, relativeTo: props.defaultRelativeTo},
|
||||
{reminder: null, relativePeriod: -1 * SECONDS_A_DAY * 7, relativeTo: props.defaultRelativeTo},
|
||||
{reminder: null, relativePeriod: -1 * SECONDS_A_DAY * 30, relativeTo: props.defaultRelativeTo},
|
||||
] as ITaskReminder[])
|
||||
const reminderDate = ref<Date | null>(null)
|
||||
|
||||
type availableForms = null | 'relative' | 'absolute'
|
||||
const showFormSwitch = ref<null | 'relative' | 'absolute'>(null)
|
||||
|
||||
const showFormSwitch = ref<availableForms>(null)
|
||||
|
||||
const activeForm = computed<availableForms>(() => {
|
||||
if (defaultRelativeTo === null) {
|
||||
const activeForm = computed(() => {
|
||||
if (props.defaultRelativeTo === null) {
|
||||
return 'absolute'
|
||||
}
|
||||
|
||||
@@ -127,7 +127,6 @@ const activeForm = computed<availableForms>(() => {
|
||||
})
|
||||
|
||||
const reminderText = computed(() => {
|
||||
|
||||
if (reminder.value.relativeTo !== null) {
|
||||
return formatReminder(reminder.value)
|
||||
}
|
||||
@@ -140,7 +139,7 @@ const reminderText = computed(() => {
|
||||
})
|
||||
|
||||
watch(
|
||||
() => modelValue,
|
||||
() => props.modelValue,
|
||||
(newReminder) => {
|
||||
if (newReminder) {
|
||||
reminder.value = newReminder
|
||||
@@ -160,12 +159,12 @@ watch(
|
||||
function updateData() {
|
||||
emit('update:modelValue', reminder.value)
|
||||
|
||||
if (clearAfterUpdate) {
|
||||
if (props.clearAfterUpdate) {
|
||||
reminder.value = new TaskReminderModel()
|
||||
}
|
||||
}
|
||||
|
||||
function setReminderDateAndClose(close) {
|
||||
function setReminderDateAndClose(close: () => void) {
|
||||
reminder.value.reminder = reminderDate.value === null
|
||||
? null
|
||||
: new Date(reminderDate.value)
|
||||
@@ -175,7 +174,7 @@ function setReminderDateAndClose(close) {
|
||||
}
|
||||
|
||||
|
||||
function setReminderFromPreset(preset, close) {
|
||||
function setReminderFromPreset(preset: ITaskReminder, close: () => void) {
|
||||
reminder.value = preset
|
||||
updateData()
|
||||
close()
|
||||
@@ -183,14 +182,14 @@ function setReminderFromPreset(preset, close) {
|
||||
|
||||
const updateDataAndMaybeClose = useDebounceFn(updateDataAndMaybeCloseNow, 500)
|
||||
|
||||
function updateDataAndMaybeCloseNow(close) {
|
||||
function updateDataAndMaybeCloseNow(close: () => void) {
|
||||
updateData()
|
||||
if (clearAfterUpdate) {
|
||||
if (props.clearAfterUpdate) {
|
||||
close()
|
||||
}
|
||||
}
|
||||
|
||||
function formatReminder(reminder: TaskReminderModel) {
|
||||
function formatReminder(reminder: ITaskReminder) {
|
||||
const period = secondsToPeriod(reminder.relativePeriod)
|
||||
|
||||
if (period.amount === 0) {
|
||||
@@ -248,6 +247,8 @@ function translateUnit(amount: number, unit: PeriodUnit): string {
|
||||
return t('time.units.weeks', amount)
|
||||
case 'years':
|
||||
return t('time.units.years', amount)
|
||||
default:
|
||||
throw new Error(`Unknown unit: ${unit}`)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -73,13 +73,13 @@ import TaskReminderModel from '@/models/taskReminder'
|
||||
import type {ITaskReminder} from '@/modelTypes/ITaskReminder'
|
||||
import {type IReminderPeriodRelativeTo, REMINDER_PERIOD_RELATIVE_TO_TYPES} from '@/types/IReminderPeriodRelativeTo'
|
||||
|
||||
const {
|
||||
modelValue,
|
||||
} = defineProps<{
|
||||
modelValue?: ITaskReminder,
|
||||
const props = defineProps<{
|
||||
modelValue: ITaskReminder,
|
||||
}>()
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [ITaskReminder]
|
||||
}>()
|
||||
|
||||
const reminder = ref<ITaskReminder>(new TaskReminderModel())
|
||||
|
||||
@@ -98,14 +98,17 @@ const period = ref<PeriodInput>({
|
||||
})
|
||||
|
||||
watch(
|
||||
() => modelValue,
|
||||
() => props.modelValue,
|
||||
(value) => {
|
||||
const p = secondsToPeriod(value?.relativePeriod)
|
||||
period.value.durationUnit = p.unit
|
||||
period.value.duration = Math.abs(p.amount)
|
||||
period.value.relativeTo = value?.relativeTo || REMINDER_PERIOD_RELATIVE_TO_TYPES.DUEDATE
|
||||
},
|
||||
{immediate: true},
|
||||
{
|
||||
immediate: true,
|
||||
deep: true,
|
||||
},
|
||||
)
|
||||
|
||||
watch(
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<div
|
||||
v-for="(r, index) in reminders"
|
||||
:key="index"
|
||||
:class="{ 'overdue': r.reminder < new Date() }"
|
||||
:class="{ 'overdue': r.reminder < now }"
|
||||
class="reminder-input"
|
||||
>
|
||||
<ReminderDetail
|
||||
@@ -40,21 +40,25 @@ import BaseButton from '@/components/base/BaseButton.vue'
|
||||
import ReminderDetail from '@/components/tasks/partials/ReminderDetail.vue'
|
||||
import type {ITask} from '@/modelTypes/ITask'
|
||||
import {REMINDER_PERIOD_RELATIVE_TO_TYPES} from '@/types/IReminderPeriodRelativeTo'
|
||||
import { useNow } from '@vueuse/core'
|
||||
|
||||
const {
|
||||
modelValue,
|
||||
disabled = false,
|
||||
} = defineProps<{
|
||||
const props = withDefaults(defineProps<{
|
||||
modelValue: ITask,
|
||||
disabled?: boolean,
|
||||
}>()
|
||||
}>(), {
|
||||
disabled: false,
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [ITask]
|
||||
}>()
|
||||
|
||||
const reminders = ref<ITaskReminder[]>([])
|
||||
|
||||
const now = useNow({interval: 1000})
|
||||
|
||||
watch(
|
||||
() => modelValue.reminders,
|
||||
() => props.modelValue.reminders,
|
||||
(newVal) => {
|
||||
reminders.value = newVal
|
||||
},
|
||||
@@ -62,19 +66,19 @@ watch(
|
||||
)
|
||||
|
||||
const defaultRelativeTo = computed(() => {
|
||||
if (typeof modelValue === 'undefined') {
|
||||
if (typeof props.modelValue === 'undefined') {
|
||||
return null
|
||||
}
|
||||
|
||||
if (modelValue?.dueDate) {
|
||||
if (props.modelValue?.dueDate) {
|
||||
return REMINDER_PERIOD_RELATIVE_TO_TYPES.DUEDATE
|
||||
}
|
||||
|
||||
if (modelValue.dueDate === null && modelValue.startDate !== null) {
|
||||
if (props.modelValue.dueDate === null && props.modelValue.startDate !== null) {
|
||||
return REMINDER_PERIOD_RELATIVE_TO_TYPES.STARTDATE
|
||||
}
|
||||
|
||||
if (modelValue.dueDate === null && modelValue.startDate === null && modelValue.endDate !== null) {
|
||||
if (props.modelValue.dueDate === null && props.modelValue.startDate === null && props.modelValue.endDate !== null) {
|
||||
return REMINDER_PERIOD_RELATIVE_TO_TYPES.ENDDATE
|
||||
}
|
||||
|
||||
@@ -83,7 +87,7 @@ const defaultRelativeTo = computed(() => {
|
||||
|
||||
function updateData() {
|
||||
emit('update:modelValue', {
|
||||
...modelValue,
|
||||
...props.modelValue,
|
||||
reminders: reminders.value,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {ref, reactive, watch, type PropType} from 'vue'
|
||||
import {ref, reactive, watch} from 'vue'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
|
||||
import {error} from '@/message'
|
||||
@@ -104,19 +104,17 @@ import type {IRepeatAfter} from '@/types/IRepeatAfter'
|
||||
import type {ITask} from '@/modelTypes/ITask'
|
||||
import TaskModel from '@/models/task'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Object as PropType<ITask>,
|
||||
default: () => ({}),
|
||||
required: false,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
const props = withDefaults(defineProps<{
|
||||
modelValue: ITask | undefined,
|
||||
disabled: boolean
|
||||
}>(), {
|
||||
modelValue: () => {},
|
||||
disabled: false,
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [value: ITask | undefined],
|
||||
}>()
|
||||
|
||||
const {t} = useI18n({useScope: 'global'})
|
||||
|
||||
@@ -134,7 +132,10 @@ watch(
|
||||
Object.assign(repeatAfter, value.repeatAfter)
|
||||
}
|
||||
},
|
||||
{immediate: true},
|
||||
{
|
||||
immediate: true,
|
||||
deep: true,
|
||||
},
|
||||
)
|
||||
|
||||
function updateData() {
|
||||
|
||||
@@ -205,34 +205,34 @@ import {useTaskStore} from '@/stores/tasks'
|
||||
import AssigneeList from '@/components/tasks/partials/AssigneeList.vue'
|
||||
import {useIntervalFn} from '@vueuse/core'
|
||||
import {playPopSound} from '@/helpers/playPop'
|
||||
import {useAuthStore} from '@/stores/auth'
|
||||
import {isEditorContentEmpty} from '@/helpers/editorContentEmpty'
|
||||
import {TASK_REPEAT_MODES} from '@/types/IRepeatMode'
|
||||
|
||||
const {
|
||||
theTask,
|
||||
isArchived = false,
|
||||
showProject = false,
|
||||
disabled = false,
|
||||
canMarkAsDone = true,
|
||||
allTasks = [],
|
||||
} = defineProps<{
|
||||
const props = withDefaults(defineProps<{
|
||||
theTask: ITask,
|
||||
isArchived?: boolean,
|
||||
showProject?: boolean,
|
||||
disabled?: boolean,
|
||||
canMarkAsDone?: boolean,
|
||||
allTasks?: ITask[],
|
||||
}>(), {
|
||||
isArchived: false,
|
||||
showProject: false,
|
||||
disabled: false,
|
||||
canMarkAsDone: true,
|
||||
allTasks: () => [],
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
'taskUpdated': [task: ITask],
|
||||
}>()
|
||||
|
||||
const emit = defineEmits(['taskUpdated'])
|
||||
|
||||
function getTaskById(taskId: number): ITask | undefined {
|
||||
if (typeof allTasks === 'undefined' || allTasks.length === 0) {
|
||||
if (typeof props.allTasks === 'undefined' || props.allTasks.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
return allTasks.find(t => t.id === taskId)
|
||||
return props.allTasks.find(t => t.id === taskId)
|
||||
}
|
||||
|
||||
const {t} = useI18n({useScope: 'global'})
|
||||
@@ -244,14 +244,17 @@ const showDefer = ref(false)
|
||||
const isRepeating = computed(() => task.value.repeatAfter.amount > 0 || (task.value.repeatAfter.amount === 0 && task.value.repeatMode === TASK_REPEAT_MODES.REPEAT_MODE_MONTH))
|
||||
|
||||
watch(
|
||||
() => theTask,
|
||||
() => props.theTask,
|
||||
newVal => {
|
||||
task.value = newVal
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
deep: true,
|
||||
},
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
task.value = theTask
|
||||
document.addEventListener('click', hideDeferDueDatePopup)
|
||||
})
|
||||
|
||||
@@ -266,7 +269,7 @@ const taskStore = useTaskStore()
|
||||
const project = computed(() => projectStore.projects[task.value.projectId])
|
||||
const projectColor = computed(() => project.value ? project.value?.hexColor : '')
|
||||
|
||||
const showProjectSeparately = computed(() => !showProject && currentProject.value?.id !== task.value.projectId && project.value)
|
||||
const showProjectSeparately = computed(() => !props.showProject && currentProject.value?.id !== task.value.projectId && project.value)
|
||||
|
||||
const currentProject = computed(() => {
|
||||
return typeof baseStore.currentProject === 'undefined' ? {
|
||||
@@ -311,7 +314,7 @@ async function markAsDone(checked: boolean, wasReverted: boolean = false) {
|
||||
return
|
||||
}
|
||||
|
||||
if (checked && useAuthStore().settings.frontendSettings.playSoundWhenDone) {
|
||||
if (checked) {
|
||||
playPopSound()
|
||||
}
|
||||
emit('taskUpdated', newTask)
|
||||
|
||||
@@ -105,8 +105,10 @@ export function useTaskList(
|
||||
const totalPages = computed(() => taskCollectionService.totalPages)
|
||||
|
||||
const tasks = ref<ITask[]>([])
|
||||
async function loadTasks() {
|
||||
tasks.value = []
|
||||
async function loadTasks(resetBeforeLoad: boolean = true) {
|
||||
if(resetBeforeLoad) {
|
||||
tasks.value = []
|
||||
}
|
||||
try {
|
||||
tasks.value = await taskCollectionService.getAll(...getAllTasksParams.value)
|
||||
} catch (e) {
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
export const calculateItemPosition = (positionBefore: number | null, positionAfter: number | null): number => {
|
||||
export const calculateItemPosition = (
|
||||
positionBefore: number | null = null,
|
||||
positionAfter: number | null = null,
|
||||
): number => {
|
||||
if (positionBefore === null) {
|
||||
if (positionAfter === null) {
|
||||
return 0
|
||||
@@ -13,6 +16,6 @@ export const calculateItemPosition = (positionBefore: number | null, positionAft
|
||||
return positionBefore + Math.pow(2, 16)
|
||||
}
|
||||
|
||||
// If we have both a task before and after it, we acually calculate the position
|
||||
// If we have both a task before and after it, we actually calculate the position
|
||||
return positionBefore + (positionAfter - positionBefore) / 2
|
||||
}
|
||||
@@ -137,6 +137,16 @@ describe('Filter Transformation', () => {
|
||||
|
||||
expect(transformed).toBe('labels in 2 || labels in l')
|
||||
})
|
||||
|
||||
it('should correctly transform the cases of fields', () => {
|
||||
const transformed = transformFilterStringForApi(
|
||||
'startdate > now',
|
||||
nullTitleToIdResolver,
|
||||
nullTitleToIdResolver,
|
||||
)
|
||||
|
||||
expect(transformed).toBe('start_date > now')
|
||||
})
|
||||
})
|
||||
|
||||
describe('To API', () => {
|
||||
|
||||
@@ -66,10 +66,16 @@ export function transformFilterStringForApi(
|
||||
labelResolver: (title: string) => number | null,
|
||||
projectResolver: (title: string) => number | null,
|
||||
): string {
|
||||
|
||||
filter = filter.trim()
|
||||
|
||||
if (filter.trim() === '') {
|
||||
if (filter === '') {
|
||||
return ''
|
||||
}
|
||||
|
||||
AVAILABLE_FILTER_FIELDS.forEach(f => {
|
||||
filter = filter.replace(new RegExp(f, 'ig'), f)
|
||||
})
|
||||
|
||||
// Transform labels to ids
|
||||
LABEL_FIELDS.forEach(field => {
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
import {useAuthStore} from '@/stores/auth'
|
||||
|
||||
import popSoundFile from '@/assets/audio/pop.mp3'
|
||||
|
||||
export const playSoundWhenDoneKey = 'playSoundWhenTaskDone'
|
||||
|
||||
export function playPopSound() {
|
||||
const playSoundWhenDone = useAuthStore().settings.frontendSettings.playSoundWhenDone
|
||||
|
||||
if (!playSoundWhenDone)
|
||||
return
|
||||
|
||||
try {
|
||||
const popSound = new Audio(popSoundFile)
|
||||
popSound.play()
|
||||
|
||||
@@ -24,7 +24,7 @@ export const SUPPORTED_LOCALES = {
|
||||
'sl-SI': 'Slovenščina',
|
||||
'pt-BR': 'Português Brasileiro',
|
||||
'hr-HR': 'Hrvatski',
|
||||
'uk-UA': 'українська',
|
||||
'uk-UA': 'Українська',
|
||||
// IMPORTANT: Also add new languages to useDayjsLanguageSync
|
||||
} as const
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
"forgotPassword": "نسيت كلمة المرور؟",
|
||||
"resetPassword": "إعادة تعيين كلمة المرور",
|
||||
"resetPasswordAction": "أرسل لي رابط إعادة تعيين كلمة المرور",
|
||||
"resetPasswordSuccess": "تحقق من صندوق الوارد الخاص بك! يجب أن تصلك رسالة مع إرشادات حول كيفية إعادة تعيين كلمة المرور الخاصة بك.",
|
||||
"resetPasswordSuccess": "Check your inbox! You should have an email with instructions on how to reset your password.",
|
||||
"passwordsDontMatch": "كلمتي المرور غير متطابقتين",
|
||||
"confirmEmailSuccess": "تم تأكيد بريدك الإلكتروني بنجاح! يمكنك تسجيل الدخول الآن.",
|
||||
"totpTitle": "رمز المصادقة الثنائية 2FA",
|
||||
@@ -72,21 +72,21 @@
|
||||
"settings": {
|
||||
"title": "الإعدادات",
|
||||
"newPasswordTitle": "تحديث كلمة مرورك",
|
||||
"newPassword": "كلمة مرور جديدة",
|
||||
"newPasswordConfirm": "تأكيد كلمة المرور الجديدة",
|
||||
"currentPassword": "كلمة المرور الحالية",
|
||||
"newPassword": "New password",
|
||||
"newPasswordConfirm": "New password confirmation",
|
||||
"currentPassword": "Current password",
|
||||
"currentPasswordPlaceholder": "كلمة المرور الحالية الخاصة بك",
|
||||
"passwordsDontMatch": "كلمة المرور الجديدة وتأكيدها غير متطابقة.",
|
||||
"passwordUpdateSuccess": "تم تحديث كلمة المرور بنجاح.",
|
||||
"updateEmailTitle": "تحديث عنوان بريدك الإلكتروني",
|
||||
"updateEmailNew": "عنوان البريد الإلكتروني الجديد",
|
||||
"updateEmailNew": "New email address",
|
||||
"updateEmailSuccess": "تم تحديث عنوان بريدك الإلكتروني بنجاح. لقد أرسلنا لك رابط لتأكيده.",
|
||||
"general": {
|
||||
"title": "الإعدادات العامة",
|
||||
"name": "الاسم",
|
||||
"newName": "الاسم الجديد",
|
||||
"name": "My Name",
|
||||
"newName": "The new name",
|
||||
"savedSuccess": "تم تحديث الإعدادات بنجاح.",
|
||||
"emailReminders": "أرسل لي تذكيرات للمهام عبر البريد الإلكتروني",
|
||||
"emailReminders": "Send me reminders for tasks via email",
|
||||
"overdueReminders": "أرسل لي ملخصاً للمهام المتأخرة ولم تنجز كل يوم",
|
||||
"discoverableByName": "السماح للمستخدمين الآخرين بإضافتي كعضو إلى الفرق أو المشاريع عند بحثهم عن اسمي",
|
||||
"discoverableByEmail": "السماح للمستخدمين الآخرين بإضافتي كعضو إلى الفرق أو المشاريع عند بحثهم عن بريدي الإلكتروني الكامل",
|
||||
@@ -95,24 +95,24 @@
|
||||
"weekStartSunday": "الأحد",
|
||||
"weekStartMonday": "الإثنين",
|
||||
"language": "اللّغة",
|
||||
"defaultProject": "المشروع الافتراضي",
|
||||
"defaultView": "Default View",
|
||||
"timezone": "منطقة التوقيت",
|
||||
"defaultProject": "Default project",
|
||||
"defaultView": "Default view",
|
||||
"timezone": "Time zone",
|
||||
"overdueTasksRemindersTime": "وقت تذكير للمهام المتأخرة بالبريد الإلكتروني",
|
||||
"filterUsedOnOverview": "الفلتر المحفوظ والمستخدم في صفحة العرض العام"
|
||||
},
|
||||
"totp": {
|
||||
"title": "رمز المصادقة الثنائية 2FA",
|
||||
"enroll": "تسجيل",
|
||||
"finishSetupPart1": "لإنهاء التثبيت، استخدم هذا الرمز السرّي في تطبيق Totp الخاص بك (Google Authenticator أو ما شابه):",
|
||||
"finishSetupPart1": "To finish your setup, use this secret in your TOTP app (Google Authenticator or similar):",
|
||||
"finishSetupPart2": "بعد ذلك، أدخل الرمز من التطبيق الخاص بك أدناه.",
|
||||
"scanQR": "أو يمكنك مسح رمز الاستجابة السريعة QR هذا:",
|
||||
"passcode": "الرمز السّري",
|
||||
"passcodePlaceholder": "الرمز السّري تم إنشاؤه بواسطة تطبيق Totp الخاص بك",
|
||||
"passcodePlaceholder": "A code generated by your TOTP application",
|
||||
"setupSuccess": "لقد قمت بنجاح بإعداد المصادقة الثنائية 2FA!",
|
||||
"enterPassword": "الرجاء إدخال كلمة المرور",
|
||||
"disable": "تعطيل المصادقة الثنائية",
|
||||
"confirmSuccess": "لقد قمت بتأكيد إعداد Totp الخاص بك بنجاح ويمكنك استخدامه من الآن فصاعداً!",
|
||||
"confirmSuccess": "You've successfully confirmed your TOTP setup and can use it from now on!",
|
||||
"disableSuccess": "تم تعطيل المصادقة الثنائية بنجاح."
|
||||
},
|
||||
"caldav": {
|
||||
@@ -163,6 +163,7 @@
|
||||
"90d": "90 يوماً",
|
||||
"permissionExplanation": "نطاق الصلاحيات المسموح به هو ما يسمح لك القيام به رمز Api.",
|
||||
"titleRequired": "العنوان إجباري",
|
||||
"permissionRequired": "Please select at least one permission from the list.",
|
||||
"expired": "انتهت صلاحية هذا الرمز {ago}.",
|
||||
"tokenCreatedSuccess": "إليك رمز Api الجديد: {token}",
|
||||
"tokenCreatedNotSeeAgain": "قم بتخزينه في مكان آمن، لن تتمكن من عرضه مرة أخرى!",
|
||||
@@ -847,7 +848,8 @@
|
||||
"delete": "حذف هذا التعليق",
|
||||
"deleteText1": "هل أنت متأكد من رغبتك بحذف هذا التعليق؟",
|
||||
"deleteSuccess": "تم حذف التعليق بنجاح.",
|
||||
"addedSuccess": "تمت إضافة التعليق بنجاح."
|
||||
"addedSuccess": "تمت إضافة التعليق بنجاح.",
|
||||
"permalink": "Copy permalink to this comment"
|
||||
},
|
||||
"deferDueDate": {
|
||||
"title": "تأجيل تاريخ الاستحقاق",
|
||||
@@ -1141,9 +1143,9 @@
|
||||
"1012": "لم يتم تأكيد عنوان البريد الإلكتروني للمستخدم.",
|
||||
"1013": "كلمة المرور الجديدة فارغة.",
|
||||
"1014": "كلمة المرور القديمة فارغة.",
|
||||
"1015": "تم تفعيل Totp مسبقاً لهذا المستخدم.",
|
||||
"1016": "لم يتم تفعيل Totp لهذا المستخدم.",
|
||||
"1017": "رمز المرور الخاص بـ Totp غير صالح.",
|
||||
"1015": "TOTP is already enabled for this user.",
|
||||
"1016": "TOTP is not enabled for this user.",
|
||||
"1017": "The TOTP passcode is invalid.",
|
||||
"1018": "إعدادات نوع الصورة الرمزية للمستخدم غير صالحة.",
|
||||
"2001": "لا يمكن أن يكون الرقم التعريفي ID فارغاً أو 0.",
|
||||
"2002": "بعض بيانات الطلب غير صالحة.",
|
||||
@@ -1153,8 +1155,8 @@
|
||||
"3006": "المشاركة في المشروع غير موجودة.",
|
||||
"3007": "يوجد مشروع بهذا المعرف مسبقاً.",
|
||||
"3008": "المشروع مؤرشف وبالتالي يمكن الوصول إليه للقراءة فقط، وهذا ينطبق أيضا على جميع المهام المرتبطة بهذا المشروع.",
|
||||
"4001": "لا يمكن ترك نص مهمة المشروع فارغاً.",
|
||||
"4002": "مهمة المشروع غير موجودة.",
|
||||
"4001": "The task title cannot be empty.",
|
||||
"4002": "The task does not exist.",
|
||||
"4003": "يجب أن تنتمي جميع إجراءات التعديل المجمعة لنفس المشروع.",
|
||||
"4004": "تحتاج إلى مهمة واحدة على الأقل عند القيام بإجراءات التعديل بالجملة.",
|
||||
"4005": "ليس لديك الصلاحية لعرض المهمة.",
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
"forgotPassword": "Forgot your password?",
|
||||
"resetPassword": "Reset your password",
|
||||
"resetPasswordAction": "Send me a password reset link",
|
||||
"resetPasswordSuccess": "Check your inbox! You should have an e-mail with instructions on how to reset your password.",
|
||||
"resetPasswordSuccess": "Check your inbox! You should have an email with instructions on how to reset your password.",
|
||||
"passwordsDontMatch": "Passwords don't match",
|
||||
"confirmEmailSuccess": "You successfully confirmed your email! You can log in now.",
|
||||
"totpTitle": "Two Factor Authentication Code",
|
||||
@@ -72,21 +72,21 @@
|
||||
"settings": {
|
||||
"title": "Settings",
|
||||
"newPasswordTitle": "Update Your Password",
|
||||
"newPassword": "New Password",
|
||||
"newPasswordConfirm": "New Password Confirmation",
|
||||
"currentPassword": "Current Password",
|
||||
"newPassword": "New password",
|
||||
"newPasswordConfirm": "New password confirmation",
|
||||
"currentPassword": "Current password",
|
||||
"currentPasswordPlaceholder": "Your current password",
|
||||
"passwordsDontMatch": "The new password and its confirmation don't match.",
|
||||
"passwordUpdateSuccess": "The password was successfully updated.",
|
||||
"updateEmailTitle": "Update Your Email Address",
|
||||
"updateEmailNew": "New Email Address",
|
||||
"updateEmailNew": "New email address",
|
||||
"updateEmailSuccess": "Your email address was successfully updated. We've sent you a link to confirm it.",
|
||||
"general": {
|
||||
"title": "General Settings",
|
||||
"name": "Name",
|
||||
"newName": "The new Name",
|
||||
"name": "My Name",
|
||||
"newName": "The new name",
|
||||
"savedSuccess": "The settings were successfully updated.",
|
||||
"emailReminders": "Send me reminders for tasks via Email",
|
||||
"emailReminders": "Send me reminders for tasks via email",
|
||||
"overdueReminders": "Send me a summary of my undone overdue tasks every day",
|
||||
"discoverableByName": "Allow other users to add me as a member to teams or projects when they search for my name",
|
||||
"discoverableByEmail": "Allow other users to add me as a member to teams or projects when they search for my full email",
|
||||
@@ -95,24 +95,24 @@
|
||||
"weekStartSunday": "Sunday",
|
||||
"weekStartMonday": "Monday",
|
||||
"language": "Language",
|
||||
"defaultProject": "Default Project",
|
||||
"defaultView": "Default View",
|
||||
"timezone": "Time Zone",
|
||||
"defaultProject": "Default project",
|
||||
"defaultView": "Default view",
|
||||
"timezone": "Time zone",
|
||||
"overdueTasksRemindersTime": "Overdue tasks reminder email time",
|
||||
"filterUsedOnOverview": "Saved filter used on the overview page"
|
||||
},
|
||||
"totp": {
|
||||
"title": "Two Factor Authentication",
|
||||
"enroll": "Enroll",
|
||||
"finishSetupPart1": "To finish your setup, use this secret in your totp app (Google Authenticator or similar):",
|
||||
"finishSetupPart1": "To finish your setup, use this secret in your TOTP app (Google Authenticator or similar):",
|
||||
"finishSetupPart2": "After that, enter a code from your app below.",
|
||||
"scanQR": "Alternatively you can scan this QR code:",
|
||||
"passcode": "Passcode",
|
||||
"passcodePlaceholder": "A code generated by your totp application",
|
||||
"passcodePlaceholder": "A code generated by your TOTP application",
|
||||
"setupSuccess": "You've successfully set up two factor authentication!",
|
||||
"enterPassword": "Please Enter Your Password",
|
||||
"disable": "Disable two factor authentication",
|
||||
"confirmSuccess": "You've successfully confirmed your totp setup and can use it from now on!",
|
||||
"confirmSuccess": "You've successfully confirmed your TOTP setup and can use it from now on!",
|
||||
"disableSuccess": "Two factor authentication was successfully disabled."
|
||||
},
|
||||
"caldav": {
|
||||
@@ -163,6 +163,7 @@
|
||||
"90d": "90 Days",
|
||||
"permissionExplanation": "Permissions allow you to scope what an api token is allowed to do.",
|
||||
"titleRequired": "The title is required",
|
||||
"permissionRequired": "Please select at least one permission from the list.",
|
||||
"expired": "This token has expired {ago}.",
|
||||
"tokenCreatedSuccess": "Here is your new api token: {token}",
|
||||
"tokenCreatedNotSeeAgain": "Store it in a secure location, you won't see it again!",
|
||||
@@ -847,7 +848,8 @@
|
||||
"delete": "Delete this comment",
|
||||
"deleteText1": "Are you sure you want to delete this comment?",
|
||||
"deleteSuccess": "The comment was deleted successfully.",
|
||||
"addedSuccess": "The comment was added successfully."
|
||||
"addedSuccess": "The comment was added successfully.",
|
||||
"permalink": "Copy permalink to this comment"
|
||||
},
|
||||
"deferDueDate": {
|
||||
"title": "Defer due date",
|
||||
@@ -1141,9 +1143,9 @@
|
||||
"1012": "Email address of the user not confirmed.",
|
||||
"1013": "New password is empty.",
|
||||
"1014": "Old password is empty.",
|
||||
"1015": "Totp is already enabled for this user.",
|
||||
"1016": "Totp is not enabled for this user.",
|
||||
"1017": "The totp passcode is invalid.",
|
||||
"1015": "TOTP is already enabled for this user.",
|
||||
"1016": "TOTP is not enabled for this user.",
|
||||
"1017": "The TOTP passcode is invalid.",
|
||||
"1018": "The user avatar type setting is invalid.",
|
||||
"2001": "ID cannot be empty or 0.",
|
||||
"2002": "Some of the request data was invalid.",
|
||||
@@ -1153,8 +1155,8 @@
|
||||
"3006": "The project share does not exist.",
|
||||
"3007": "A project with this identifier already exists.",
|
||||
"3008": "The project is archived and can therefore only be accessed read only. This is also true for all tasks associated with this project.",
|
||||
"4001": "The project task text cannot be empty.",
|
||||
"4002": "The project task does not exist.",
|
||||
"4001": "The task title cannot be empty.",
|
||||
"4002": "The task does not exist.",
|
||||
"4003": "All bulk editing tasks must belong to the same project.",
|
||||
"4004": "Need at least one task when bulk editing tasks.",
|
||||
"4005": "You do not have the right to see the task.",
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
"forgotPassword": "Zapomenuté heslo?",
|
||||
"resetPassword": "Obnovit heslo",
|
||||
"resetPasswordAction": "Poslat odkaz na obnovení hesla",
|
||||
"resetPasswordSuccess": "Zkontrolujte doručenou poštu! Měli byste mít e-mail s pokyny, jak obnovit své heslo.",
|
||||
"resetPasswordSuccess": "Check your inbox! You should have an email with instructions on how to reset your password.",
|
||||
"passwordsDontMatch": "Hesla se neshodují",
|
||||
"confirmEmailSuccess": "Úspěšně jste potvrdili svůj e-mail! Nyní se můžete přihlásit.",
|
||||
"totpTitle": "Kód dvoufaktorového ověření",
|
||||
@@ -72,21 +72,21 @@
|
||||
"settings": {
|
||||
"title": "Nastavení",
|
||||
"newPasswordTitle": "Aktualizujte své heslo",
|
||||
"newPassword": "Nové heslo",
|
||||
"newPasswordConfirm": "Potvrzení nového hesla",
|
||||
"currentPassword": "Stávající heslo",
|
||||
"newPassword": "New password",
|
||||
"newPasswordConfirm": "New password confirmation",
|
||||
"currentPassword": "Current password",
|
||||
"currentPasswordPlaceholder": "Vaše současné heslo",
|
||||
"passwordsDontMatch": "Nové heslo se neshoduje s potvrzením hesla.",
|
||||
"passwordUpdateSuccess": "Heslo bylo úspěšně změněno.",
|
||||
"updateEmailTitle": "Aktualizujte svou e-mailovou adresu",
|
||||
"updateEmailNew": "Nová e-mailová adresa",
|
||||
"updateEmailNew": "New email address",
|
||||
"updateEmailSuccess": "Vaše e-mailová adresa byla úspěšně aktualizována. Poslali jsme vám odkaz pro její potvrzení.",
|
||||
"general": {
|
||||
"title": "Obecná nastavení",
|
||||
"name": "Jméno",
|
||||
"newName": "Nové jméno",
|
||||
"name": "My Name",
|
||||
"newName": "The new name",
|
||||
"savedSuccess": "Nastavení bylo úspěšně aktualizováno.",
|
||||
"emailReminders": "Posílat mi připomenutí pro úkoly e-mailem",
|
||||
"emailReminders": "Send me reminders for tasks via email",
|
||||
"overdueReminders": "Pošlete mi každý den shrnutí mých zpožděných úkolů",
|
||||
"discoverableByName": "Umožnit ostatním uživatelům přidat mě jako člena do týmů nebo projektů, když hledají mé jméno",
|
||||
"discoverableByEmail": "Umožnit ostatním uživatelům, aby mě přidali jako člena do týmů nebo projektů, když hledají můj úplný e-mail",
|
||||
@@ -95,24 +95,24 @@
|
||||
"weekStartSunday": "Neděle",
|
||||
"weekStartMonday": "Pondělí",
|
||||
"language": "Jazyk",
|
||||
"defaultProject": "Výchozí projekt",
|
||||
"defaultView": "Default View",
|
||||
"timezone": "Časové pásmo",
|
||||
"defaultProject": "Default project",
|
||||
"defaultView": "Default view",
|
||||
"timezone": "Time zone",
|
||||
"overdueTasksRemindersTime": "Čas odeslání emailu o zpožděných úkolech",
|
||||
"filterUsedOnOverview": "Uložený filtr použitý na stránce přehledu"
|
||||
},
|
||||
"totp": {
|
||||
"title": "Dvoufaktorové ověření",
|
||||
"enroll": "Zápis",
|
||||
"finishSetupPart1": "Chcete-li dokončit nastavení, použijte tento tajný klíč ve vaší aplikaci (Google Authenticator nebo podobné):",
|
||||
"finishSetupPart1": "To finish your setup, use this secret in your TOTP app (Google Authenticator or similar):",
|
||||
"finishSetupPart2": "Poté zadejte kód z vaší aplikace.",
|
||||
"scanQR": "Případně můžete naskenovat tento QR kód:",
|
||||
"passcode": "Přístupový kód",
|
||||
"passcodePlaceholder": "Kód vygenerovaný vaší aplikací",
|
||||
"passcodePlaceholder": "A code generated by your TOTP application",
|
||||
"setupSuccess": "Úspěšně jste nastavili dvoufaktorové ověření!",
|
||||
"enterPassword": "Zadejte prosím heslo",
|
||||
"disable": "Zakázat dvoufaktorové ověření",
|
||||
"confirmSuccess": "Úspěšně jste potvrdili nastavení a od nynějška jej můžete používat!",
|
||||
"confirmSuccess": "You've successfully confirmed your TOTP setup and can use it from now on!",
|
||||
"disableSuccess": "Dvoufázové ověření bylo úspěšně vypnuto."
|
||||
},
|
||||
"caldav": {
|
||||
@@ -163,6 +163,7 @@
|
||||
"90d": "90 dní",
|
||||
"permissionExplanation": "Oprávnění vám umožní nastavit, k čemu lze api token použít.",
|
||||
"titleRequired": "Je vyžadován název",
|
||||
"permissionRequired": "Please select at least one permission from the list.",
|
||||
"expired": "Platnost tohoto tokenu vypršela {ago}.",
|
||||
"tokenCreatedSuccess": "Zde je tvůj nový api token: {token}",
|
||||
"tokenCreatedNotSeeAgain": "Ulož jej na zabezpečeném místě, už ho znovu neuvidíš!",
|
||||
@@ -847,7 +848,8 @@
|
||||
"delete": "Smazat tento komentář",
|
||||
"deleteText1": "Opravdu chcete smazat tento komentář?",
|
||||
"deleteSuccess": "Komentář byl úspěšně odstraněn.",
|
||||
"addedSuccess": "Komentář byl úspěšně přidán."
|
||||
"addedSuccess": "Komentář byl úspěšně přidán.",
|
||||
"permalink": "Copy permalink to this comment"
|
||||
},
|
||||
"deferDueDate": {
|
||||
"title": "Odložit datum dokončení",
|
||||
@@ -1141,9 +1143,9 @@
|
||||
"1012": "E-mailová adresa uživatele nebyla potvrzena.",
|
||||
"1013": "Nové heslo je prázdné.",
|
||||
"1014": "Staré heslo je prázdné.",
|
||||
"1015": "Totp je již pro tohoto uživatele povolen.",
|
||||
"1016": "Totp není pro tohoto uživatele povolen.",
|
||||
"1017": "Kód totp je neplatný.",
|
||||
"1015": "TOTP is already enabled for this user.",
|
||||
"1016": "TOTP is not enabled for this user.",
|
||||
"1017": "The TOTP passcode is invalid.",
|
||||
"1018": "Nastavení typu avatara uživatele je neplatné.",
|
||||
"2001": "ID nemůže být prázdné nebo 0.",
|
||||
"2002": "Některé údaje požadavku byly neplatné.",
|
||||
@@ -1153,8 +1155,8 @@
|
||||
"3006": "Sdílení projektu neexistuje.",
|
||||
"3007": "Projekt s tímto identifikátorem již existuje.",
|
||||
"3008": "Projekt je archivován, a proto je přístupný pouze pro čtení. To platí i pro všechny úkoly spojené s tímto projektem.",
|
||||
"4001": "Text úkolu projektu nemůže být prázdný.",
|
||||
"4002": "Úkol projektu neexistuje.",
|
||||
"4001": "The task title cannot be empty.",
|
||||
"4002": "The task does not exist.",
|
||||
"4003": "Všechny úkoly pro hromadnou úpravu musí patřit do stejného projektu.",
|
||||
"4004": "Při hromadných úpravách úkolů je potřeba alespoň jeden úkol.",
|
||||
"4005": "Nemáte právo vidět tento úkol.",
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
"forgotPassword": "Har du glemt din adgangskode?",
|
||||
"resetPassword": "Nulstil din adgangskode",
|
||||
"resetPasswordAction": "Send mig et link til nulstilling af adgangskode",
|
||||
"resetPasswordSuccess": "Tjek din indbakke! Du bør have en e-mail med instruktioner om, hvordan du nulstiller din adgangskode.",
|
||||
"resetPasswordSuccess": "Check your inbox! You should have an email with instructions on how to reset your password.",
|
||||
"passwordsDontMatch": "Adgangskoderne er ikke ens",
|
||||
"confirmEmailSuccess": "Du har bekræftet din e-mail! Du kan logge ind nu.",
|
||||
"totpTitle": "To Faktor Godkendelseskode",
|
||||
@@ -72,21 +72,21 @@
|
||||
"settings": {
|
||||
"title": "Indstillinger",
|
||||
"newPasswordTitle": "Opdater din adgangskode",
|
||||
"newPassword": "Ny adgangskode",
|
||||
"newPasswordConfirm": "Bekræftelse Af Ny Adgangskode",
|
||||
"currentPassword": "Nuværende Adgangskode",
|
||||
"newPassword": "New password",
|
||||
"newPasswordConfirm": "New password confirmation",
|
||||
"currentPassword": "Current password",
|
||||
"currentPasswordPlaceholder": "Din nuværende adgangskode",
|
||||
"passwordsDontMatch": "Din nye adgangskode og bekræftelsesadgangskoden er ikke ens.",
|
||||
"passwordUpdateSuccess": "Adgangskoden blev ændret.",
|
||||
"updateEmailTitle": "Opdater din e-mailadresse",
|
||||
"updateEmailNew": "Ny e-mailadresse",
|
||||
"updateEmailNew": "New email address",
|
||||
"updateEmailSuccess": "Din e-mailadresse blev opdateret. Vi har sendt dig et link til at bekræfte den.",
|
||||
"general": {
|
||||
"title": "Generelle Indstillinger",
|
||||
"name": "Navn",
|
||||
"newName": "Det nye navn",
|
||||
"name": "My Name",
|
||||
"newName": "The new name",
|
||||
"savedSuccess": "Indstillingerne er gemt.",
|
||||
"emailReminders": "Send mig påmindelser for opgaver via e-mail",
|
||||
"emailReminders": "Send me reminders for tasks via email",
|
||||
"overdueReminders": "Send mig en oversigt over mine ufærdige opgaver hver dag",
|
||||
"discoverableByName": "Allow other users to add me as a member to teams or projects when they search for my name",
|
||||
"discoverableByEmail": "Allow other users to add me as a member to teams or projects when they search for my full email",
|
||||
@@ -95,24 +95,24 @@
|
||||
"weekStartSunday": "Søndag",
|
||||
"weekStartMonday": "Mandag",
|
||||
"language": "Sprog",
|
||||
"defaultProject": "Default Project",
|
||||
"defaultView": "Default View",
|
||||
"timezone": "Tidszone",
|
||||
"defaultProject": "Default project",
|
||||
"defaultView": "Default view",
|
||||
"timezone": "Time zone",
|
||||
"overdueTasksRemindersTime": "Forfaldne opgaver påmindelse e-mail tidspunkt",
|
||||
"filterUsedOnOverview": "Saved filter used on the overview page"
|
||||
},
|
||||
"totp": {
|
||||
"title": "To-faktor godkendelse",
|
||||
"enroll": "Tilmeld",
|
||||
"finishSetupPart1": "For at afslutte din opsætning, brug denne nøgle i din totp app (Google Authenticator eller tilsvarende):",
|
||||
"finishSetupPart1": "To finish your setup, use this secret in your TOTP app (Google Authenticator or similar):",
|
||||
"finishSetupPart2": "Herefter skal du indtaste en kode fra din app nedenfor.",
|
||||
"scanQR": "Alternativt kan du scanne denne QR-kode:",
|
||||
"passcode": "Kode",
|
||||
"passcodePlaceholder": "En kode genereret af din totp applikation",
|
||||
"passcodePlaceholder": "A code generated by your TOTP application",
|
||||
"setupSuccess": "Du har aktiveret to-faktor autentificering!",
|
||||
"enterPassword": "Indtast venligst din adgangskode",
|
||||
"disable": "Deaktiver to-faktor autentificering",
|
||||
"confirmSuccess": "Du har bekræftet din totp opsætning og kan bruge den fra nu af!",
|
||||
"confirmSuccess": "You've successfully confirmed your TOTP setup and can use it from now on!",
|
||||
"disableSuccess": "To faktor godkendelse blev deaktiveret."
|
||||
},
|
||||
"caldav": {
|
||||
@@ -163,6 +163,7 @@
|
||||
"90d": "90 Days",
|
||||
"permissionExplanation": "Permissions allow you to scope what an api token is allowed to do.",
|
||||
"titleRequired": "The title is required",
|
||||
"permissionRequired": "Please select at least one permission from the list.",
|
||||
"expired": "This token has expired {ago}.",
|
||||
"tokenCreatedSuccess": "Here is your new api token: {token}",
|
||||
"tokenCreatedNotSeeAgain": "Store it in a secure location, you won't see it again!",
|
||||
@@ -847,7 +848,8 @@
|
||||
"delete": "Slet denne kommentar",
|
||||
"deleteText1": "Er du sikker på du vil slette denne kommentar?",
|
||||
"deleteSuccess": "Kommentaren blev slettet.",
|
||||
"addedSuccess": "Din kommentar blev tilføjet."
|
||||
"addedSuccess": "Din kommentar blev tilføjet.",
|
||||
"permalink": "Copy permalink to this comment"
|
||||
},
|
||||
"deferDueDate": {
|
||||
"title": "Udsæt forfaldsdato",
|
||||
@@ -1141,9 +1143,9 @@
|
||||
"1012": "E-mail adresse for brugeren ikke bekræftet.",
|
||||
"1013": "Ny adgangskode er tom.",
|
||||
"1014": "Gammel adgangskode er tom.",
|
||||
"1015": "Totp er allerede aktiveret for denne bruger.",
|
||||
"1016": "Totp er ikke aktiveret for denne bruger.",
|
||||
"1017": "Totp koden er ugyldig.",
|
||||
"1015": "TOTP is already enabled for this user.",
|
||||
"1016": "TOTP is not enabled for this user.",
|
||||
"1017": "The TOTP passcode is invalid.",
|
||||
"1018": "Brugerens avatartype indstilling er ugyldig.",
|
||||
"2001": "ID kan ikke være tomt eller 0.",
|
||||
"2002": "Nogle af forespørgselsdataene var ugyldige.",
|
||||
@@ -1153,8 +1155,8 @@
|
||||
"3006": "The project share does not exist.",
|
||||
"3007": "A project with this identifier already exists.",
|
||||
"3008": "The project is archived and can therefore only be accessed read only. This is also true for all tasks associated with this project.",
|
||||
"4001": "The project task text cannot be empty.",
|
||||
"4002": "The project task does not exist.",
|
||||
"4001": "The task title cannot be empty.",
|
||||
"4002": "The task does not exist.",
|
||||
"4003": "All bulk editing tasks must belong to the same project.",
|
||||
"4004": "Der skal være mindst én opgave for at masseredigere opgaver.",
|
||||
"4005": "Du har ikke rettigheder til at se opgaven.",
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
"forgotPassword": "Passwort vergessen?",
|
||||
"resetPassword": "Setze dein Passwort zurück",
|
||||
"resetPasswordAction": "Sende mir einen Link zum Zurücksetzen des Passworts",
|
||||
"resetPasswordSuccess": "Prüfe deinen Posteingang! Du solltest eine E-Mail mit Anweisungen zum Zurücksetzen deines Passworts erhalten haben.",
|
||||
"resetPasswordSuccess": "Überprüfe deinen Posteingang! Du solltest eine E-Mail mit Anweisungen zum Zurücksetzen deines Passwortes haben.",
|
||||
"passwordsDontMatch": "Passwörter stimmen nicht überein",
|
||||
"confirmEmailSuccess": "Du hast deine E-Mail-Adresse erfolgreich bestätigt! Du kannst dich jetzt einloggen.",
|
||||
"totpTitle": "Zwei-Faktor-Authentifizierungscode",
|
||||
@@ -83,7 +83,7 @@
|
||||
"updateEmailSuccess": "Deine E-Mail-Adresse wurde erfolgreich aktualisiert. Wir haben dir einen Link zur Bestätigung geschickt.",
|
||||
"general": {
|
||||
"title": "Allgemeine Einstellungen",
|
||||
"name": "Name",
|
||||
"name": "Mein Name",
|
||||
"newName": "Neuer Name",
|
||||
"savedSuccess": "Die Einstellungen wurden erfolgreich aktualisiert.",
|
||||
"emailReminders": "Erinnerungen an Aufgaben per E-Mail senden",
|
||||
@@ -95,7 +95,7 @@
|
||||
"weekStartSunday": "Sonntag",
|
||||
"weekStartMonday": "Montag",
|
||||
"language": "Sprache",
|
||||
"defaultProject": "Standard-Projekt",
|
||||
"defaultProject": "Standardprojekt",
|
||||
"defaultView": "Standardansicht",
|
||||
"timezone": "Zeitzone",
|
||||
"overdueTasksRemindersTime": "Zeit der E-Mail-Zusammenfassung der überfälligen Aufgaben",
|
||||
@@ -104,15 +104,15 @@
|
||||
"totp": {
|
||||
"title": "Zwei-Faktor-Authentifizierung",
|
||||
"enroll": "Aktivieren",
|
||||
"finishSetupPart1": "Um die Aktivierung zu vollenden, benutze diesen Token in deiner TOTP App (Google Authenticator oder ähnlich):",
|
||||
"finishSetupPart1": "Um die Aktivierung abzuschließen, benutze diesen Token in deiner TOTP App (Google Authenticator oder ähnliche):",
|
||||
"finishSetupPart2": "Gib anschließend unten einen Code aus deiner Anwendung ein.",
|
||||
"scanQR": "Alternativ kannst du auch diesen QR-Code scannen:",
|
||||
"passcode": "Code",
|
||||
"passcodePlaceholder": "Ein von deiner TOTP-App generierter code",
|
||||
"passcodePlaceholder": "Ein von deiner TOTP-App generierter Code",
|
||||
"setupSuccess": "Du hast die Zwei-Faktor-Authentifizierung erfolgreich eingerichtet!",
|
||||
"enterPassword": "Bitte gib dein Passwort ein",
|
||||
"disable": "Zwei-Faktor-Authentifizierung deaktivieren",
|
||||
"confirmSuccess": "TOTP wurde verifiziert und ist nun verwendbar.",
|
||||
"confirmSuccess": "TOTP wurde verifiziert und ist nun verwendbar!",
|
||||
"disableSuccess": "Die Zwei-Faktor-Authentifizierung wurde erfolgreich deaktiviert."
|
||||
},
|
||||
"caldav": {
|
||||
@@ -163,6 +163,7 @@
|
||||
"90d": "90 Tage",
|
||||
"permissionExplanation": "Mit Berechtigungen kannst du einschränken, was ein API-Token tun darf.",
|
||||
"titleRequired": "Titel ist erforderlich",
|
||||
"permissionRequired": "Bitte wähle mindestens eine Berechtigung aus der Liste.",
|
||||
"expired": "Dieses Token ist {ago} abgelaufen.",
|
||||
"tokenCreatedSuccess": "Hier ist dein neues API Token: {token}",
|
||||
"tokenCreatedNotSeeAgain": "Speichere es an einem sicheren Ort, du wirst es nicht mehr sehen!",
|
||||
@@ -387,19 +388,19 @@
|
||||
"secretDocs": "In der Dokumentation findest du weitere Informationen zum Umgang mit Schlüsseln."
|
||||
},
|
||||
"views": {
|
||||
"header": "Edit views",
|
||||
"title": "Title",
|
||||
"actions": "Actions",
|
||||
"kind": "Kind",
|
||||
"bucketConfigMode": "Bucket configuration mode",
|
||||
"bucketConfig": "Bucket configuration",
|
||||
"bucketConfigManual": "Manual",
|
||||
"header": "Ansichten bearbeiten",
|
||||
"title": "Titel",
|
||||
"actions": "Aktionen",
|
||||
"kind": "Ansicht",
|
||||
"bucketConfigMode": "Bucket-Konfigurationsmodus",
|
||||
"bucketConfig": "Bucket-Konfiguration",
|
||||
"bucketConfigManual": "Manuell",
|
||||
"filter": "Filter",
|
||||
"create": "Create view",
|
||||
"createSuccess": "The view was created successfully.",
|
||||
"titleRequired": "Please provide a title.",
|
||||
"delete": "Delete this view",
|
||||
"deleteText": "Are you sure you want to remove this view? It will no longer be possible to use it to view tasks in this project. This action won't delete any tasks. This cannot be undone!",
|
||||
"create": "Ansicht erstellen",
|
||||
"createSuccess": "Die Ansicht wurde erfolgreich erstellt.",
|
||||
"titleRequired": "Bitte gib einen Titel ein.",
|
||||
"delete": "Diese Ansicht löschen",
|
||||
"deleteText": "Bist du sicher, dass du diese Ansicht entfernen möchtest? Es wird nicht mehr möglich sein, sie zu verwenden, um Aufgaben in diesem Projekt anzuzeigen. Diese Aktion löscht keine Aufgaben. Dies kann nicht rückgängig gemacht werden!",
|
||||
"deleteSuccess": "Diese Ansicht wurde erfolgreich gelöscht.",
|
||||
"onlyAdminsCanEdit": "Nur Projektadministrator:innen können Ansichten bearbeiten.",
|
||||
"updateSuccess": "Die Ansicht wurde erfolgreich aktualisiert."
|
||||
@@ -847,7 +848,8 @@
|
||||
"delete": "Diesen Kommentar löschen",
|
||||
"deleteText1": "Bist du sicher, dass du diesen Kommentar löschen willst?",
|
||||
"deleteSuccess": "Der Kommentar wurde erfolgreich gelöscht.",
|
||||
"addedSuccess": "Der Kommentar wurde erfolgreich hinzugefügt."
|
||||
"addedSuccess": "Der Kommentar wurde erfolgreich hinzugefügt.",
|
||||
"permalink": "Permalink zu diesem Kommentar kopieren"
|
||||
},
|
||||
"deferDueDate": {
|
||||
"title": "Fälligkeitsdatum verschieben",
|
||||
@@ -1072,7 +1074,7 @@
|
||||
"createProject": "Projekt erstellen",
|
||||
"cantArchiveIsDefault": "Du kannst dieses Projekt nicht archivieren, da es dein Standardprojekt ist.",
|
||||
"cantDeleteIsDefault": "Du kannst dieses Projekt nicht löschen, da es dein Standardprojekt ist.",
|
||||
"views": "Views"
|
||||
"views": "Ansichten"
|
||||
},
|
||||
"apiConfig": {
|
||||
"url": "Vikunja-URL",
|
||||
@@ -1141,8 +1143,8 @@
|
||||
"1012": "Die E-Mail-Adresse der:s Benutzer:in ist nicht bestätigt.",
|
||||
"1013": "Das neue Passwort ist leer.",
|
||||
"1014": "Das alte Passwort ist leer.",
|
||||
"1015": "Dieser Benutzer verwendet TOTP bereits.",
|
||||
"1016": "Dieser Benutzer verwendet TOTP nicht.",
|
||||
"1015": "Diese:r Benutzer:in verwendet TOTP bereits.",
|
||||
"1016": "Diese:r Benutzer:in verwendet TOTP nicht.",
|
||||
"1017": "Falscher TOTP Code.",
|
||||
"1018": "Die Avatareinstellungen sind falsch.",
|
||||
"2001": "Die ID kann nicht leer oder 0 sein.",
|
||||
@@ -1153,7 +1155,7 @@
|
||||
"3006": "Diese Linkfreigabe existiert nicht.",
|
||||
"3007": "Ein Projekt mit diesem Bezeichner existiert bereits.",
|
||||
"3008": "Dieses Projekt ist archiviert und kann deshalb nur gelesen werden. Dies gilt auch für alle Aufgaben, die mit diesem Projekt verbunden sind.",
|
||||
"4001": "Der Aufgabentitel kann nicht leer sein.",
|
||||
"4001": "Der Titel darf nicht leer sein.",
|
||||
"4002": "Diese Aufgabe existiert nicht.",
|
||||
"4003": "Alle Massenbearbeitungen an Aufgaben müssen zum selben Projekt gehören.",
|
||||
"4004": "Es benötigt mindestens einen Task, um eine Massenänderung durchzuführen.",
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
"forgotPassword": "Passwort vergessen?",
|
||||
"resetPassword": "Setz diis Passwort zrugg",
|
||||
"resetPasswordAction": "Schick mir en Passwort zruggsetz Link",
|
||||
"resetPasswordSuccess": "Prüfe deinen Posteingang! Du solltest eine E-Mail mit Anweisungen zum Zurücksetzen deines Passworts erhalten haben.",
|
||||
"resetPasswordSuccess": "Überprüfe deinen Posteingang! Du solltest eine E-Mail mit Anweisungen zum Zurücksetzen deines Passwortes haben.",
|
||||
"passwordsDontMatch": "Passwörter stimmed nid überiih",
|
||||
"confirmEmailSuccess": "Du hesch dini E-Mail erfolgriich bestätigt! Du chasch dich jetzt iihlogge.",
|
||||
"totpTitle": "Zweifaktor Authentifizierigs Ziffere",
|
||||
@@ -72,21 +72,21 @@
|
||||
"settings": {
|
||||
"title": "Iihstellige",
|
||||
"newPasswordTitle": "Diis Passwort aktualisierä",
|
||||
"newPassword": "Neus Passwort",
|
||||
"newPasswordConfirm": "Neui Passwort Bestätigung",
|
||||
"currentPassword": "Momentans Passwort",
|
||||
"newPassword": "Neues Passwort",
|
||||
"newPasswordConfirm": "Bestätigung des neuen Passworts",
|
||||
"currentPassword": "Aktuelles Passwort",
|
||||
"currentPasswordPlaceholder": "Diis jetzige Passwort",
|
||||
"passwordsDontMatch": "Dis neue Passwort und siini Bestätigung stimmed nid überiih.",
|
||||
"passwordUpdateSuccess": "Dis Passwort isch erfolgriich aktualisiert wordä.",
|
||||
"updateEmailTitle": "Aktualisiere deine E-Mail-Adresse",
|
||||
"updateEmailNew": "Neui E-Mail Adrässä",
|
||||
"updateEmailNew": "Neue E-Mail-Adresse",
|
||||
"updateEmailSuccess": "Dini E-Mail Adrässä isch erfolgriich gänderet worde. Mir hend dir en Link gschickt, um si zu bestätigä.",
|
||||
"general": {
|
||||
"title": "Allgemeini Iihstellige",
|
||||
"name": "Namä",
|
||||
"newName": "De neu Namä",
|
||||
"name": "Mein Name",
|
||||
"newName": "Neuer Name",
|
||||
"savedSuccess": "Die Iihstellige sind erfolgriich aktualisiert wordä.",
|
||||
"emailReminders": "Schick mir e Errinnerig für Uufgabe per E-Mail",
|
||||
"emailReminders": "Erinnerungen an Aufgaben per E-Mail senden",
|
||||
"overdueReminders": "Sende mir jeden Tag eine Zusammenfassung meiner überfälligen Aufgaben",
|
||||
"discoverableByName": "Erlaube anderen Benutzer:innen, mich als Mitglied zu Teams oder Projekten hinzuzufügen, wenn sie nach meinem Namen suchen",
|
||||
"discoverableByEmail": "Erlaube anderen Benutzer:innen, mich als Mitglied zu Teams oder Projekten hinzuzufügen, wenn sie nach meiner vollständigen E-Mail Adresse suchen",
|
||||
@@ -95,7 +95,7 @@
|
||||
"weekStartSunday": "Sunntig",
|
||||
"weekStartMonday": "Määntig",
|
||||
"language": "Sproch",
|
||||
"defaultProject": "Standard-Projekt",
|
||||
"defaultProject": "Standardprojekt",
|
||||
"defaultView": "Standardansicht",
|
||||
"timezone": "Zeitzone",
|
||||
"overdueTasksRemindersTime": "Zeit der E-Mail-Zusammenfassung der überfälligen Aufgaben",
|
||||
@@ -104,15 +104,15 @@
|
||||
"totp": {
|
||||
"title": "Zweifaktor Authentifizierig",
|
||||
"enroll": "Aktiviere",
|
||||
"finishSetupPart1": "Um dini Iihrichtig fertigzstelle, benutz das Gheimniss i dinere Authentifizierigs App (Google Authentifikator oder ähnliches):",
|
||||
"finishSetupPart1": "Um die Aktivierung abzuschließen, benutze diesen Token in deiner TOTP App (Google Authenticator oder ähnliche):",
|
||||
"finishSetupPart2": "Denoch, gib de Code vo dinere App une iih.",
|
||||
"scanQR": "Alternativ chasch au de QR Code scanne:",
|
||||
"passcode": "Code",
|
||||
"passcodePlaceholder": "En vo dinere Authentifikator App generierte code",
|
||||
"passcodePlaceholder": "Ein von deiner TOTP-App generierter Code",
|
||||
"setupSuccess": "Du hast die Zwei-Faktor-Authentifizierung erfolgreich eingerichtet!",
|
||||
"enterPassword": "Bitte gib diis Passwort iih",
|
||||
"disable": "Zweifaktor Authentifizierig uusschalte",
|
||||
"confirmSuccess": "Du hesch erfolgriich dini Zweifaktor Authentifizierig aktiviert und chasch se vo jetzt au benutze!",
|
||||
"confirmSuccess": "TOTP wurde verifiziert und ist nun verwendbar!",
|
||||
"disableSuccess": "Die Zwei-Faktor-Authentifizierung wurde erfolgreich deaktiviert."
|
||||
},
|
||||
"caldav": {
|
||||
@@ -163,6 +163,7 @@
|
||||
"90d": "90 Tage",
|
||||
"permissionExplanation": "Mit Berechtigungen kannst du einschränken, was ein API-Token tun darf.",
|
||||
"titleRequired": "Titel ist erforderlich",
|
||||
"permissionRequired": "Bitte wähle mindestens eine Berechtigung aus der Liste.",
|
||||
"expired": "Dieses Token ist {ago} abgelaufen.",
|
||||
"tokenCreatedSuccess": "Hier ist dein neues API Token: {token}",
|
||||
"tokenCreatedNotSeeAgain": "Speichere es an einem sicheren Ort, du wirst es nicht mehr sehen!",
|
||||
@@ -387,19 +388,19 @@
|
||||
"secretDocs": "In der Dokumentation findest du weitere Informationen zum Umgang mit Schlüsseln."
|
||||
},
|
||||
"views": {
|
||||
"header": "Edit views",
|
||||
"title": "Title",
|
||||
"actions": "Actions",
|
||||
"kind": "Kind",
|
||||
"bucketConfigMode": "Bucket configuration mode",
|
||||
"bucketConfig": "Bucket configuration",
|
||||
"bucketConfigManual": "Manual",
|
||||
"header": "Ansichten bearbeiten",
|
||||
"title": "Titel",
|
||||
"actions": "Aktionen",
|
||||
"kind": "Ansicht",
|
||||
"bucketConfigMode": "Bucket-Konfigurationsmodus",
|
||||
"bucketConfig": "Bucket-Konfiguration",
|
||||
"bucketConfigManual": "Manuell",
|
||||
"filter": "Filter",
|
||||
"create": "Create view",
|
||||
"createSuccess": "The view was created successfully.",
|
||||
"titleRequired": "Please provide a title.",
|
||||
"delete": "Delete this view",
|
||||
"deleteText": "Are you sure you want to remove this view? It will no longer be possible to use it to view tasks in this project. This action won't delete any tasks. This cannot be undone!",
|
||||
"create": "Ansicht erstellen",
|
||||
"createSuccess": "Die Ansicht wurde erfolgreich erstellt.",
|
||||
"titleRequired": "Bitte gib einen Titel ein.",
|
||||
"delete": "Diese Ansicht löschen",
|
||||
"deleteText": "Bist du sicher, dass du diese Ansicht entfernen möchtest? Es wird nicht mehr möglich sein, sie zu verwenden, um Aufgaben in diesem Projekt anzuzeigen. Diese Aktion löscht keine Aufgaben. Dies kann nicht rückgängig gemacht werden!",
|
||||
"deleteSuccess": "Diese Ansicht wurde erfolgreich gelöscht.",
|
||||
"onlyAdminsCanEdit": "Nur Projektadministrator:innen können Ansichten bearbeiten.",
|
||||
"updateSuccess": "Die Ansicht wurde erfolgreich aktualisiert."
|
||||
@@ -847,7 +848,8 @@
|
||||
"delete": "De Kommentar chüble",
|
||||
"deleteText1": "Bisch du dir sicher, dass du de Kommentar chüble wetsch?",
|
||||
"deleteSuccess": "Der Kommentar wurde erfolgreich gelöscht.",
|
||||
"addedSuccess": "Din Kommentar isch erfolgriich hinzuegfüegt worde."
|
||||
"addedSuccess": "Din Kommentar isch erfolgriich hinzuegfüegt worde.",
|
||||
"permalink": "Permalink zu diesem Kommentar kopieren"
|
||||
},
|
||||
"deferDueDate": {
|
||||
"title": "Fälligkeitsdatum verschiebe",
|
||||
@@ -1072,7 +1074,7 @@
|
||||
"createProject": "Projekt erstellen",
|
||||
"cantArchiveIsDefault": "Du kannst dieses Projekt nicht archivieren, da es dein Standardprojekt ist.",
|
||||
"cantDeleteIsDefault": "Du kannst dieses Projekt nicht löschen, da es dein Standardprojekt ist.",
|
||||
"views": "Views"
|
||||
"views": "Ansichten"
|
||||
},
|
||||
"apiConfig": {
|
||||
"url": "Vikunja URL",
|
||||
@@ -1141,9 +1143,9 @@
|
||||
"1012": "Die E-Mail vo dem Benutzer isch nid bestätigt.",
|
||||
"1013": "Neus Passwort isch läär.",
|
||||
"1014": "Alts Passwort isch läär.",
|
||||
"1015": "Totp isch für de Benutzer scho aktiv.",
|
||||
"1016": "Totp isch für de Benutzer nid aktiv.",
|
||||
"1017": "De Totp Code nid gültig.",
|
||||
"1015": "Diese:r Benutzer:in verwendet TOTP bereits.",
|
||||
"1016": "Diese:r Benutzer:in verwendet TOTP nicht.",
|
||||
"1017": "Falscher TOTP Code.",
|
||||
"1018": "Die Benutzer Profilbild Iihstellige sind nid gültig.",
|
||||
"2001": "ID chann nid leer oder 0 sii.",
|
||||
"2002": "Ebbis vo de Ahfragedate isch ungültig.",
|
||||
@@ -1153,7 +1155,7 @@
|
||||
"3006": "Diese Linkfreigabe existiert nicht.",
|
||||
"3007": "Ein Projekt mit diesem Bezeichner existiert bereits.",
|
||||
"3008": "Dieses Projekt ist archiviert und kann deshalb nur gelesen werden. Dies gilt auch für alle Aufgaben, die mit diesem Projekt verbunden sind.",
|
||||
"4001": "Der Aufgabentitel kann nicht leer sein.",
|
||||
"4001": "Der Titel darf nicht leer sein.",
|
||||
"4002": "Diese Aufgabe existiert nicht.",
|
||||
"4003": "Alle Massenbearbeitungen an Aufgaben müssen zum selben Projekt gehören.",
|
||||
"4004": "Es bruucht mindestens ei Uufgab, um e Masseänderig durezfüehre.",
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
"forgotPassword": "Forgot your password?",
|
||||
"resetPassword": "Reset your password",
|
||||
"resetPasswordAction": "Send me a password reset link",
|
||||
"resetPasswordSuccess": "Check your inbox! You should have an e-mail with instructions on how to reset your password.",
|
||||
"resetPasswordSuccess": "Check your inbox! You should have an email with instructions on how to reset your password.",
|
||||
"passwordsDontMatch": "Passwords don't match",
|
||||
"confirmEmailSuccess": "You successfully confirmed your email! You can log in now.",
|
||||
"totpTitle": "Two Factor Authentication Code",
|
||||
@@ -72,21 +72,21 @@
|
||||
"settings": {
|
||||
"title": "Settings",
|
||||
"newPasswordTitle": "Update Your Password",
|
||||
"newPassword": "New Password",
|
||||
"newPasswordConfirm": "New Password Confirmation",
|
||||
"currentPassword": "Current Password",
|
||||
"newPassword": "New password",
|
||||
"newPasswordConfirm": "New password confirmation",
|
||||
"currentPassword": "Current password",
|
||||
"currentPasswordPlaceholder": "Your current password",
|
||||
"passwordsDontMatch": "The new password and its confirmation don't match.",
|
||||
"passwordUpdateSuccess": "The password was successfully updated.",
|
||||
"updateEmailTitle": "Update Your Email Address",
|
||||
"updateEmailNew": "New Email Address",
|
||||
"updateEmailNew": "New email address",
|
||||
"updateEmailSuccess": "Your email address was successfully updated. We've sent you a link to confirm it.",
|
||||
"general": {
|
||||
"title": "General Settings",
|
||||
"name": "Name",
|
||||
"newName": "The new Name",
|
||||
"name": "My Name",
|
||||
"newName": "The new name",
|
||||
"savedSuccess": "The settings were successfully updated.",
|
||||
"emailReminders": "Send me reminders for tasks via Email",
|
||||
"emailReminders": "Send me reminders for tasks via email",
|
||||
"overdueReminders": "Send me a summary of my undone overdue tasks every day",
|
||||
"discoverableByName": "Allow other users to add me as a member to teams or projects when they search for my name",
|
||||
"discoverableByEmail": "Allow other users to add me as a member to teams or projects when they search for my full email",
|
||||
@@ -95,24 +95,24 @@
|
||||
"weekStartSunday": "Sunday",
|
||||
"weekStartMonday": "Monday",
|
||||
"language": "Language",
|
||||
"defaultProject": "Default Project",
|
||||
"defaultView": "Default View",
|
||||
"timezone": "Time Zone",
|
||||
"defaultProject": "Default project",
|
||||
"defaultView": "Default view",
|
||||
"timezone": "Time zone",
|
||||
"overdueTasksRemindersTime": "Overdue tasks reminder email time",
|
||||
"filterUsedOnOverview": "Saved filter used on the overview page"
|
||||
},
|
||||
"totp": {
|
||||
"title": "Two Factor Authentication",
|
||||
"enroll": "Enroll",
|
||||
"finishSetupPart1": "To finish your setup, use this secret in your totp app (Google Authenticator or similar):",
|
||||
"finishSetupPart1": "To finish your setup, use this secret in your TOTP app (Google Authenticator or similar):",
|
||||
"finishSetupPart2": "After that, enter a code from your app below.",
|
||||
"scanQR": "Alternatively you can scan this QR code:",
|
||||
"passcode": "Passcode",
|
||||
"passcodePlaceholder": "A code generated by your totp application",
|
||||
"passcodePlaceholder": "A code generated by your TOTP application",
|
||||
"setupSuccess": "You've successfully set up two factor authentication!",
|
||||
"enterPassword": "Please Enter Your Password",
|
||||
"disable": "Disable two factor authentication",
|
||||
"confirmSuccess": "You've successfully confirmed your totp setup and can use it from now on!",
|
||||
"confirmSuccess": "You've successfully confirmed your TOTP setup and can use it from now on!",
|
||||
"disableSuccess": "Two factor authentication was successfully disabled."
|
||||
},
|
||||
"caldav": {
|
||||
@@ -163,6 +163,7 @@
|
||||
"90d": "90 Days",
|
||||
"permissionExplanation": "Permissions allow you to scope what an api token is allowed to do.",
|
||||
"titleRequired": "The title is required",
|
||||
"permissionRequired": "Please select at least one permission from the list.",
|
||||
"expired": "This token has expired {ago}.",
|
||||
"tokenCreatedSuccess": "Here is your new api token: {token}",
|
||||
"tokenCreatedNotSeeAgain": "Store it in a secure location, you won't see it again!",
|
||||
@@ -1141,9 +1142,9 @@
|
||||
"1012": "Email address of the user not confirmed.",
|
||||
"1013": "New password is empty.",
|
||||
"1014": "Old password is empty.",
|
||||
"1015": "Totp is already enabled for this user.",
|
||||
"1016": "Totp is not enabled for this user.",
|
||||
"1017": "The totp passcode is invalid.",
|
||||
"1015": "TOTP is already enabled for this user.",
|
||||
"1016": "TOTP is not enabled for this user.",
|
||||
"1017": "The TOTP passcode is invalid.",
|
||||
"1018": "The user avatar type setting is invalid.",
|
||||
"2001": "ID cannot be empty or 0.",
|
||||
"2002": "Some of the request data was invalid.",
|
||||
@@ -1153,8 +1154,8 @@
|
||||
"3006": "The project share does not exist.",
|
||||
"3007": "A project with this identifier already exists.",
|
||||
"3008": "The project is archived and can therefore only be accessed read only. This is also true for all tasks associated with this project.",
|
||||
"4001": "The project task text cannot be empty.",
|
||||
"4002": "The project task does not exist.",
|
||||
"4001": "The task title cannot be empty.",
|
||||
"4002": "The task does not exist.",
|
||||
"4003": "All bulk editing tasks must belong to the same project.",
|
||||
"4004": "Need at least one task when bulk editing tasks.",
|
||||
"4005": "You do not have the right to see the task.",
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
"forgotPassword": "Forgot your password?",
|
||||
"resetPassword": "Reset your password",
|
||||
"resetPasswordAction": "Send me a password reset link",
|
||||
"resetPasswordSuccess": "Check your inbox! You should have an e-mail with instructions on how to reset your password.",
|
||||
"resetPasswordSuccess": "Check your inbox! You should have an email with instructions on how to reset your password.",
|
||||
"passwordsDontMatch": "Passwords don't match",
|
||||
"confirmEmailSuccess": "You successfully confirmed your email! You can log in now.",
|
||||
"totpTitle": "Two Factor Authentication Code",
|
||||
@@ -72,21 +72,21 @@
|
||||
"settings": {
|
||||
"title": "Settings",
|
||||
"newPasswordTitle": "Update Your Password",
|
||||
"newPassword": "New Password",
|
||||
"newPasswordConfirm": "New Password Confirmation",
|
||||
"currentPassword": "Current Password",
|
||||
"newPassword": "New password",
|
||||
"newPasswordConfirm": "New password confirmation",
|
||||
"currentPassword": "Current password",
|
||||
"currentPasswordPlaceholder": "Your current password",
|
||||
"passwordsDontMatch": "The new password and its confirmation don't match.",
|
||||
"passwordUpdateSuccess": "The password was successfully updated.",
|
||||
"updateEmailTitle": "Update Your Email Address",
|
||||
"updateEmailNew": "New Email Address",
|
||||
"updateEmailNew": "New email address",
|
||||
"updateEmailSuccess": "Your email address was successfully updated. We've sent you a link to confirm it.",
|
||||
"general": {
|
||||
"title": "General Settings",
|
||||
"name": "Name",
|
||||
"newName": "The new Name",
|
||||
"name": "My Name",
|
||||
"newName": "The new name",
|
||||
"savedSuccess": "The settings were successfully updated.",
|
||||
"emailReminders": "Send me reminders for tasks via Email",
|
||||
"emailReminders": "Send me reminders for tasks via email",
|
||||
"overdueReminders": "Send me a summary of my undone overdue tasks every day",
|
||||
"discoverableByName": "Allow other users to add me as a member to teams or projects when they search for my name",
|
||||
"discoverableByEmail": "Allow other users to add me as a member to teams or projects when they search for my full email",
|
||||
@@ -95,24 +95,24 @@
|
||||
"weekStartSunday": "Sunday",
|
||||
"weekStartMonday": "Monday",
|
||||
"language": "Language",
|
||||
"defaultProject": "Default Project",
|
||||
"defaultView": "Default View",
|
||||
"timezone": "Time Zone",
|
||||
"defaultProject": "Default project",
|
||||
"defaultView": "Default view",
|
||||
"timezone": "Time zone",
|
||||
"overdueTasksRemindersTime": "Overdue tasks reminder email time",
|
||||
"filterUsedOnOverview": "Saved filter used on the overview page"
|
||||
},
|
||||
"totp": {
|
||||
"title": "Two Factor Authentication",
|
||||
"enroll": "Enroll",
|
||||
"finishSetupPart1": "To finish your setup, use this secret in your totp app (Google Authenticator or similar):",
|
||||
"finishSetupPart1": "To finish your setup, use this secret in your TOTP app (Google Authenticator or similar):",
|
||||
"finishSetupPart2": "After that, enter a code from your app below.",
|
||||
"scanQR": "Alternatively you can scan this QR code:",
|
||||
"passcode": "Passcode",
|
||||
"passcodePlaceholder": "A code generated by your totp application",
|
||||
"passcodePlaceholder": "A code generated by your TOTP application",
|
||||
"setupSuccess": "You've successfully set up two factor authentication!",
|
||||
"enterPassword": "Please Enter Your Password",
|
||||
"disable": "Disable two factor authentication",
|
||||
"confirmSuccess": "You've successfully confirmed your totp setup and can use it from now on!",
|
||||
"confirmSuccess": "You've successfully confirmed your TOTP setup and can use it from now on!",
|
||||
"disableSuccess": "Two factor authentication was successfully disabled."
|
||||
},
|
||||
"caldav": {
|
||||
@@ -163,6 +163,7 @@
|
||||
"90d": "90 Days",
|
||||
"permissionExplanation": "Permissions allow you to scope what an api token is allowed to do.",
|
||||
"titleRequired": "The title is required",
|
||||
"permissionRequired": "Please select at least one permission from the list.",
|
||||
"expired": "This token has expired {ago}.",
|
||||
"tokenCreatedSuccess": "Here is your new api token: {token}",
|
||||
"tokenCreatedNotSeeAgain": "Store it in a secure location, you won't see it again!",
|
||||
@@ -847,7 +848,8 @@
|
||||
"delete": "Delete this comment",
|
||||
"deleteText1": "Are you sure you want to delete this comment?",
|
||||
"deleteSuccess": "The comment was deleted successfully.",
|
||||
"addedSuccess": "The comment was added successfully."
|
||||
"addedSuccess": "The comment was added successfully.",
|
||||
"permalink": "Copy permalink to this comment"
|
||||
},
|
||||
"deferDueDate": {
|
||||
"title": "Defer due date",
|
||||
@@ -1141,9 +1143,9 @@
|
||||
"1012": "Email address of the user not confirmed.",
|
||||
"1013": "New password is empty.",
|
||||
"1014": "Old password is empty.",
|
||||
"1015": "Totp is already enabled for this user.",
|
||||
"1016": "Totp is not enabled for this user.",
|
||||
"1017": "The totp passcode is invalid.",
|
||||
"1015": "TOTP is already enabled for this user.",
|
||||
"1016": "TOTP is not enabled for this user.",
|
||||
"1017": "The TOTP passcode is invalid.",
|
||||
"1018": "The user avatar type setting is invalid.",
|
||||
"2001": "ID cannot be empty or 0.",
|
||||
"2002": "Some of the request data was invalid.",
|
||||
@@ -1153,8 +1155,8 @@
|
||||
"3006": "The project share does not exist.",
|
||||
"3007": "A project with this identifier already exists.",
|
||||
"3008": "The project is archived and can therefore only be accessed read only. This is also true for all tasks associated with this project.",
|
||||
"4001": "The project task text cannot be empty.",
|
||||
"4002": "The project task does not exist.",
|
||||
"4001": "The task title cannot be empty.",
|
||||
"4002": "The task does not exist.",
|
||||
"4003": "All bulk editing tasks must belong to the same project.",
|
||||
"4004": "Need at least one task when bulk editing tasks.",
|
||||
"4005": "You do not have the right to see the task.",
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
"forgotPassword": "¿Olvidaste tu contraseña?",
|
||||
"resetPassword": "Restablecer tu contraseña",
|
||||
"resetPasswordAction": "Envíame un enlace para restablecer la contraseña",
|
||||
"resetPasswordSuccess": "¡Revisa tu bandeja de entrada! Debes tener un correo electrónico con instrucciones para restablecer tu contraseña.",
|
||||
"resetPasswordSuccess": "Check your inbox! You should have an email with instructions on how to reset your password.",
|
||||
"passwordsDontMatch": "Las contraseñas no coinciden",
|
||||
"confirmEmailSuccess": "¡Has confirmado correctamente tu correo electrónico! Ya puedes conectarte.",
|
||||
"totpTitle": "Código de autenticación de dos factores",
|
||||
@@ -72,21 +72,21 @@
|
||||
"settings": {
|
||||
"title": "Ajustes",
|
||||
"newPasswordTitle": "Actualiza tu contraseña",
|
||||
"newPassword": "Nueva contraseña",
|
||||
"newPasswordConfirm": "Confirmar contraseña",
|
||||
"currentPassword": "Contraseña actual",
|
||||
"newPassword": "New password",
|
||||
"newPasswordConfirm": "New password confirmation",
|
||||
"currentPassword": "Current password",
|
||||
"currentPasswordPlaceholder": "Tu contraseña actual",
|
||||
"passwordsDontMatch": "La nueva contraseña y su confirmación no coinciden.",
|
||||
"passwordUpdateSuccess": "La contraseña se actualizó con éxito.",
|
||||
"updateEmailTitle": "Actualiza tu Dirección de Correo Electrónico",
|
||||
"updateEmailNew": "Nueva dirección de correo electrónico",
|
||||
"updateEmailNew": "New email address",
|
||||
"updateEmailSuccess": "Tu dirección de correo electrónico se ha actualizado con éxito. Te hemos enviado un enlace para confirmarlo.",
|
||||
"general": {
|
||||
"title": "Ajustes Generales",
|
||||
"name": "Nombre",
|
||||
"newName": "El nombre nuevo",
|
||||
"name": "My Name",
|
||||
"newName": "The new name",
|
||||
"savedSuccess": "Ajustes actualizados con éxito.",
|
||||
"emailReminders": "Enviarme recordatorios de tareas vía Correo Electrónico",
|
||||
"emailReminders": "Send me reminders for tasks via email",
|
||||
"overdueReminders": "Enviarme un resumen de mis tareas pendientes todos los días",
|
||||
"discoverableByName": "Permitir que otros usuarios me agreguen como miembro a equipos o proyectos cuando busquen mi nombre",
|
||||
"discoverableByEmail": "Permitir a otros usuarios añadirme como miembro a equipos o proyectos cuando busquen mi email completo",
|
||||
@@ -95,24 +95,24 @@
|
||||
"weekStartSunday": "Domingo",
|
||||
"weekStartMonday": "Lunes",
|
||||
"language": "Idioma",
|
||||
"defaultProject": "Proyecto por defecto",
|
||||
"defaultView": "Default View",
|
||||
"timezone": "Zona Horaria",
|
||||
"defaultProject": "Default project",
|
||||
"defaultView": "Default view",
|
||||
"timezone": "Time zone",
|
||||
"overdueTasksRemindersTime": "Hora de envío del correo electrónico de tareas pendientes",
|
||||
"filterUsedOnOverview": "Filtro guardado utilizado en la página de resumen"
|
||||
},
|
||||
"totp": {
|
||||
"title": "Autenticación de dos factores",
|
||||
"enroll": "Inscribirse",
|
||||
"finishSetupPart1": "Para finalizar tu configuración, utiliza este secreto en tu aplicación totp (Google Authenticator o similar):",
|
||||
"finishSetupPart1": "To finish your setup, use this secret in your TOTP app (Google Authenticator or similar):",
|
||||
"finishSetupPart2": "Después, introduce un código de tu aplicación abajo.",
|
||||
"scanQR": "Alternativamente, escanea este código QR:",
|
||||
"passcode": "Código de acceso",
|
||||
"passcodePlaceholder": "Un código generado por tu aplicación totp",
|
||||
"passcodePlaceholder": "A code generated by your TOTP application",
|
||||
"setupSuccess": "¡Has configurado con éxito la autenticación de dos factores!",
|
||||
"enterPassword": "Por favor, introduce tu contraseña",
|
||||
"disable": "Deshabilitar la autenticación de dos factores",
|
||||
"confirmSuccess": "¡Has confirmado con éxito tu configuración totp y puedes usarla a partir de ahora!",
|
||||
"confirmSuccess": "You've successfully confirmed your TOTP setup and can use it from now on!",
|
||||
"disableSuccess": "La autenticación de dos factores desactivada con éxito."
|
||||
},
|
||||
"caldav": {
|
||||
@@ -163,6 +163,7 @@
|
||||
"90d": "90 Days",
|
||||
"permissionExplanation": "Permissions allow you to scope what an api token is allowed to do.",
|
||||
"titleRequired": "The title is required",
|
||||
"permissionRequired": "Please select at least one permission from the list.",
|
||||
"expired": "This token has expired {ago}.",
|
||||
"tokenCreatedSuccess": "Here is your new api token: {token}",
|
||||
"tokenCreatedNotSeeAgain": "Store it in a secure location, you won't see it again!",
|
||||
@@ -847,7 +848,8 @@
|
||||
"delete": "Eliminar este comentario",
|
||||
"deleteText1": "¿Está seguro que desea eliminar este comentario?",
|
||||
"deleteSuccess": "El comentario fue eliminado con éxito.",
|
||||
"addedSuccess": "El comentario fue añadido con éxito."
|
||||
"addedSuccess": "El comentario fue añadido con éxito.",
|
||||
"permalink": "Copy permalink to this comment"
|
||||
},
|
||||
"deferDueDate": {
|
||||
"title": "Aplazar fecha de vencimiento",
|
||||
@@ -1141,9 +1143,9 @@
|
||||
"1012": "La dirección de correo electrónico del usuario no está confirmada.",
|
||||
"1013": "La nueva contraseña está vacía.",
|
||||
"1014": "La contraseña antigua está vacía.",
|
||||
"1015": "Totp ya está habilitado para este usuario.",
|
||||
"1016": "Totp no está habilitado para este usuario.",
|
||||
"1017": "El código Totp es inválido.",
|
||||
"1015": "TOTP is already enabled for this user.",
|
||||
"1016": "TOTP is not enabled for this user.",
|
||||
"1017": "The TOTP passcode is invalid.",
|
||||
"1018": "La configuración del tipo de avatar de usuario es inválida.",
|
||||
"2001": "El ID no puede estar vacío o ser 0.",
|
||||
"2002": "Algunos datos de la solicitud fueron inválidos.",
|
||||
@@ -1153,8 +1155,8 @@
|
||||
"3006": "La compartición del proyecto no existe.",
|
||||
"3007": "Ya existe un proyecto con este identificador.",
|
||||
"3008": "El proyecto está archivado y por lo tanto sólo se puede acceder a él en modo lectura. Esto también se aplica a las tareas asociadas con este proyecto.",
|
||||
"4001": "El texto de la tarea del proyecto no puede estar vacío.",
|
||||
"4002": "La tarea del proyecto no existe.",
|
||||
"4001": "The task title cannot be empty.",
|
||||
"4002": "The task does not exist.",
|
||||
"4003": "Todas las tareas de edición en masa deben pertenecer al mismo proyecto.",
|
||||
"4004": "Se necesita al menos una tarea cuando se editan tareas masivamente.",
|
||||
"4005": "No tiene permiso para ver la tarea.",
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
"forgotPassword": "Mot de passe oublié ?",
|
||||
"resetPassword": "Réinitialiser votre mot de passe",
|
||||
"resetPasswordAction": "M’envoyer un lien de réinitialisation du mot de passe",
|
||||
"resetPasswordSuccess": "Vérifiez votre boîte de réception ! Vous devriez y trouver un courriel contenant les instructions pour réinitialiser votre mot de passe.",
|
||||
"resetPasswordSuccess": "Check your inbox! You should have an email with instructions on how to reset your password.",
|
||||
"passwordsDontMatch": "Les mots de passe ne correspondent pas",
|
||||
"confirmEmailSuccess": "Vous avez bien confirmé votre adresse ! Vous pouvez maintenant vous connecter.",
|
||||
"totpTitle": "Code d’authentification à deux facteurs",
|
||||
@@ -72,21 +72,21 @@
|
||||
"settings": {
|
||||
"title": "Paramètres",
|
||||
"newPasswordTitle": "Mets à jour ton mot de passe",
|
||||
"newPassword": "Nouveau mot de passe",
|
||||
"newPasswordConfirm": "Confirmation du nouveau mot de passe",
|
||||
"currentPassword": "Mot de passe actuel",
|
||||
"newPassword": "New password",
|
||||
"newPasswordConfirm": "New password confirmation",
|
||||
"currentPassword": "Current password",
|
||||
"currentPasswordPlaceholder": "Votre mot de passe actuel",
|
||||
"passwordsDontMatch": "Le nouveau mot de passe et sa confirmation ne correspondent pas.",
|
||||
"passwordUpdateSuccess": "Le mot de passe a bien été mis à jour.",
|
||||
"updateEmailTitle": "Mettre à jour votre adresse électronique",
|
||||
"updateEmailNew": "Nouvelle adresse électronique",
|
||||
"updateEmailNew": "New email address",
|
||||
"updateEmailSuccess": "L’adresse électronique a bien été mise à jour. Nous vous avons envoyé un lien pour confirmer ce changement.",
|
||||
"general": {
|
||||
"title": "Paramètres généraux",
|
||||
"name": "Nom",
|
||||
"newName": "Le nouveau nom",
|
||||
"name": "My Name",
|
||||
"newName": "The new name",
|
||||
"savedSuccess": "Paramètres mis à jour.",
|
||||
"emailReminders": "M’envoyer des rappels de tâches par courriel",
|
||||
"emailReminders": "Send me reminders for tasks via email",
|
||||
"overdueReminders": "Envoyez-moi un résumé de mes tâches en retard inachevées chaque jour",
|
||||
"discoverableByName": "Autoriser d’autres utilisateurs ou utilisatrices à m’ajouter en tant que membre aux équipes ou aux projets lorsqu’ils recherchent mon nom",
|
||||
"discoverableByEmail": "Autoriser d’autres utilisateurs ou utilisatrices à m’ajouter en tant que membre aux équipes ou aux projets lorsqu’ils recherchent mon adresse électronique complète",
|
||||
@@ -95,24 +95,24 @@
|
||||
"weekStartSunday": "dimanche",
|
||||
"weekStartMonday": "lundi",
|
||||
"language": "Langue",
|
||||
"defaultProject": "Projet par défaut",
|
||||
"defaultView": "Default View",
|
||||
"timezone": "Fuseau horaire",
|
||||
"defaultProject": "Default project",
|
||||
"defaultView": "Default view",
|
||||
"timezone": "Time zone",
|
||||
"overdueTasksRemindersTime": "Heure du courriel de rappel des tâches en retard",
|
||||
"filterUsedOnOverview": "Filtre enregistré utilisé sur la vue d’ensemble"
|
||||
},
|
||||
"totp": {
|
||||
"title": "Authentification à deux facteurs",
|
||||
"enroll": "S’inscrire",
|
||||
"finishSetupPart1": "Pour terminer votre configuration, utilisez ce secret dans votre application de mot de passe à usage unique :",
|
||||
"finishSetupPart1": "To finish your setup, use this secret in your TOTP app (Google Authenticator or similar):",
|
||||
"finishSetupPart2": "Après cela, saisissez un code issu de votre application ci-dessous.",
|
||||
"scanQR": "Vous pouvez également scanner ce code QR :",
|
||||
"passcode": "Code",
|
||||
"passcodePlaceholder": "Un code généré par votre application de mot de passe à usage unique",
|
||||
"passcodePlaceholder": "A code generated by your TOTP application",
|
||||
"setupSuccess": "Vous avez maintenant configuré l’authentification à deux facteurs !",
|
||||
"enterPassword": "Saisissez votre mot de passe",
|
||||
"disable": "Désactiver l’authentification à deux facteurs",
|
||||
"confirmSuccess": "La configuration du mot de passe à usage unique est confirmée et vous pouvez l’utiliser dès à présent !",
|
||||
"confirmSuccess": "You've successfully confirmed your TOTP setup and can use it from now on!",
|
||||
"disableSuccess": "L’authentification à deux facteurs a bien été désactivée."
|
||||
},
|
||||
"caldav": {
|
||||
@@ -163,6 +163,7 @@
|
||||
"90d": "90 Days",
|
||||
"permissionExplanation": "Permissions allow you to scope what an api token is allowed to do.",
|
||||
"titleRequired": "The title is required",
|
||||
"permissionRequired": "Please select at least one permission from the list.",
|
||||
"expired": "This token has expired {ago}.",
|
||||
"tokenCreatedSuccess": "Here is your new api token: {token}",
|
||||
"tokenCreatedNotSeeAgain": "Store it in a secure location, you won't see it again!",
|
||||
@@ -847,7 +848,8 @@
|
||||
"delete": "Supprimer ce commentaire",
|
||||
"deleteText1": "Supprimer ce commentaire ?",
|
||||
"deleteSuccess": "Le commentaire a bien été supprimé.",
|
||||
"addedSuccess": "Commentaire ajouté."
|
||||
"addedSuccess": "Commentaire ajouté.",
|
||||
"permalink": "Copy permalink to this comment"
|
||||
},
|
||||
"deferDueDate": {
|
||||
"title": "Reporter la date d’échéance",
|
||||
@@ -1141,9 +1143,9 @@
|
||||
"1012": "L’adresse électronique de l’utilisateur·rice n’est pas confirmée.",
|
||||
"1013": "Le nouveau mot de passe est vide.",
|
||||
"1014": "L’ancien mot de passe est vide.",
|
||||
"1015": "Cette personne utilise déjà un mot de passe à usage unique.",
|
||||
"1016": "Le mot de passe à usage unique n’est pas activé pour cet utilisateur ou cette utilisatrice.",
|
||||
"1017": "Code à usage unique invalide.",
|
||||
"1015": "TOTP is already enabled for this user.",
|
||||
"1016": "TOTP is not enabled for this user.",
|
||||
"1017": "The TOTP passcode is invalid.",
|
||||
"1018": "Le paramètre du type d’avatar de l’utilisateur·rice est invalide.",
|
||||
"2001": "L’identifiant ne peut pas être vide ou égal à 0.",
|
||||
"2002": "Certaines des données de la requête étaient invalides.",
|
||||
@@ -1153,8 +1155,8 @@
|
||||
"3006": "Le partage de ce projet n’existe pas.",
|
||||
"3007": "Un projet avec cet identifiant existe déjà.",
|
||||
"3008": "Le projet est archivé et ne peut donc être consulté qu’en lecture seule. Ceci est également vrai pour toutes les tâches associées à ce projet.",
|
||||
"4001": "Le texte de la tâche du projet ne peut pas être vide.",
|
||||
"4002": "La tâche de projet n’existe pas.",
|
||||
"4001": "The task title cannot be empty.",
|
||||
"4002": "The task does not exist.",
|
||||
"4003": "Toutes les tâches de modification en bloc doivent appartenir au même projet.",
|
||||
"4004": "Besoin d’au moins une tâche lors de la modification en bloc de tâches.",
|
||||
"4005": "Vous n’avez pas le droit de voir la tâche.",
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
"forgotPassword": "Zaboravili ste lozinku?",
|
||||
"resetPassword": "Ponovno postavite lozinku",
|
||||
"resetPasswordAction": "Pošalji mi vezu za ponovno postavljanje lozinke",
|
||||
"resetPasswordSuccess": "Provjerite svoj inbox! Trebali ste dobiti e-poštu s uputama kako poništiti lozinku.",
|
||||
"resetPasswordSuccess": "Check your inbox! You should have an email with instructions on how to reset your password.",
|
||||
"passwordsDontMatch": "Lozinke se ne podudaraju",
|
||||
"confirmEmailSuccess": "Uspješno ste potvrdili svoju e-poštu! Sada se možete prijaviti.",
|
||||
"totpTitle": "Dvostruki kod za provjeru autentifikacije",
|
||||
@@ -72,21 +72,21 @@
|
||||
"settings": {
|
||||
"title": "Postavke",
|
||||
"newPasswordTitle": "Ažurirajte svoju lozinku",
|
||||
"newPassword": "Nova lozinka",
|
||||
"newPasswordConfirm": "Potvrda nove lozinke",
|
||||
"currentPassword": "Trenutna lozinka",
|
||||
"newPassword": "New password",
|
||||
"newPasswordConfirm": "New password confirmation",
|
||||
"currentPassword": "Current password",
|
||||
"currentPasswordPlaceholder": "Vaša trenutna lozinka",
|
||||
"passwordsDontMatch": "Nova lozinka i njezina potvrda ne odgovaraju.",
|
||||
"passwordUpdateSuccess": "Lozinka je uspješno ažurirana.",
|
||||
"updateEmailTitle": "Ažurirajte e-mail adresu",
|
||||
"updateEmailNew": "Nova adresa e-pošte",
|
||||
"updateEmailNew": "New email address",
|
||||
"updateEmailSuccess": "Vaša email adresa je uspješno ažurirana. Poslali smo vam poveznicu da ju potvrdite.",
|
||||
"general": {
|
||||
"title": "Opće postavke",
|
||||
"name": "Ime",
|
||||
"newName": "Novo ime",
|
||||
"name": "My Name",
|
||||
"newName": "The new name",
|
||||
"savedSuccess": "Postavke su uspješno spremljene.",
|
||||
"emailReminders": "Šalji mi podsjetnike za zadatke putem e-pošte",
|
||||
"emailReminders": "Send me reminders for tasks via email",
|
||||
"overdueReminders": "Šalji mi sažetak mojih neobavljenih zakašnjelih zadataka svaki dan",
|
||||
"discoverableByName": "Dopusti drugim korisnicima da me dodaju kao člana u timove ili projekte kada traže moje ime",
|
||||
"discoverableByEmail": "Dopusti drugim korisnicima da me dodaju kao člana u timove ili projekte kada traže moju punu e-poštu",
|
||||
@@ -95,24 +95,24 @@
|
||||
"weekStartSunday": "Nedjelju",
|
||||
"weekStartMonday": "Ponedjeljak",
|
||||
"language": "Jezik",
|
||||
"defaultProject": "Zadani projekt",
|
||||
"defaultView": "Default View",
|
||||
"timezone": "Vremenska Zona",
|
||||
"defaultProject": "Default project",
|
||||
"defaultView": "Default view",
|
||||
"timezone": "Time zone",
|
||||
"overdueTasksRemindersTime": "Vrijeme podsjetnika za zakašnjele zadatke putem e-pošte",
|
||||
"filterUsedOnOverview": "Spremljeni filtar korišten na stranici pregleda"
|
||||
},
|
||||
"totp": {
|
||||
"title": "Dvofaktorska autentikacija",
|
||||
"enroll": "Učlani se",
|
||||
"finishSetupPart1": "Da biste dovršili svoje postavljanje, upotrijebite ovu tajnu u svojoj totp aplikaciji (Google Authenticator ili slično):",
|
||||
"finishSetupPart1": "To finish your setup, use this secret in your TOTP app (Google Authenticator or similar):",
|
||||
"finishSetupPart2": "Nakon toga unesite kod iz svoje aplikacije u nastavku.",
|
||||
"scanQR": "Alternativno, možete skenirati ovaj QR kod:",
|
||||
"passcode": "Lozinka",
|
||||
"passcodePlaceholder": "Kod koji je generirala vaša totp aplikacija",
|
||||
"passcodePlaceholder": "A code generated by your TOTP application",
|
||||
"setupSuccess": "Uspješno ste postavili dvostruku provjeru autentičnosti!",
|
||||
"enterPassword": "Molimo unesite vašu lozinku",
|
||||
"disable": "Isključite dvofaktorsku provjeru autentičnosti",
|
||||
"confirmSuccess": "Uspješno ste potvrdili svoju totp postavku i od sada je možete koristiti!",
|
||||
"confirmSuccess": "You've successfully confirmed your TOTP setup and can use it from now on!",
|
||||
"disableSuccess": "Dvostruka provjera autentičnosti uspješno je onemogućena."
|
||||
},
|
||||
"caldav": {
|
||||
@@ -163,6 +163,7 @@
|
||||
"90d": "90 dana",
|
||||
"permissionExplanation": "Dopuštenja vam omogućuju da odredite što api token smije raditi.",
|
||||
"titleRequired": "Naslov je obavezan",
|
||||
"permissionRequired": "Please select at least one permission from the list.",
|
||||
"expired": "Ovaj je token istekao {ago}.",
|
||||
"tokenCreatedSuccess": "Evo vašeg novog api tokena: {token}",
|
||||
"tokenCreatedNotSeeAgain": "Čuvajte ga na sigurnom mjestu, nećete ga više vidjeti!",
|
||||
@@ -847,7 +848,8 @@
|
||||
"delete": "Delete this comment",
|
||||
"deleteText1": "Are you sure you want to delete this comment?",
|
||||
"deleteSuccess": "The comment was deleted successfully.",
|
||||
"addedSuccess": "The comment was added successfully."
|
||||
"addedSuccess": "The comment was added successfully.",
|
||||
"permalink": "Copy permalink to this comment"
|
||||
},
|
||||
"deferDueDate": {
|
||||
"title": "Defer due date",
|
||||
@@ -1141,9 +1143,9 @@
|
||||
"1012": "Email address of the user not confirmed.",
|
||||
"1013": "New password is empty.",
|
||||
"1014": "Old password is empty.",
|
||||
"1015": "Totp is already enabled for this user.",
|
||||
"1016": "Totp is not enabled for this user.",
|
||||
"1017": "The totp passcode is invalid.",
|
||||
"1015": "TOTP is already enabled for this user.",
|
||||
"1016": "TOTP is not enabled for this user.",
|
||||
"1017": "The TOTP passcode is invalid.",
|
||||
"1018": "The user avatar type setting is invalid.",
|
||||
"2001": "ID cannot be empty or 0.",
|
||||
"2002": "Some of the request data was invalid.",
|
||||
@@ -1153,8 +1155,8 @@
|
||||
"3006": "The project share does not exist.",
|
||||
"3007": "A project with this identifier already exists.",
|
||||
"3008": "The project is archived and can therefore only be accessed read only. This is also true for all tasks associated with this project.",
|
||||
"4001": "The project task text cannot be empty.",
|
||||
"4002": "The project task does not exist.",
|
||||
"4001": "The task title cannot be empty.",
|
||||
"4002": "The task does not exist.",
|
||||
"4003": "All bulk editing tasks must belong to the same project.",
|
||||
"4004": "Need at least one task when bulk editing tasks.",
|
||||
"4005": "You do not have the right to see the task.",
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
"forgotPassword": "Elfelejtette a jelszavát?",
|
||||
"resetPassword": "Jelszó visszaállítása",
|
||||
"resetPasswordAction": "Jelszó-visszaállítási linket kérek",
|
||||
"resetPasswordSuccess": "Ellenőrizze postaládáját! Kapnia kell egy e-mailt a jelszó visszaállításához szükséges utasításokkal.",
|
||||
"resetPasswordSuccess": "Check your inbox! You should have an email with instructions on how to reset your password.",
|
||||
"passwordsDontMatch": "Jelszavak nem egyeznek",
|
||||
"confirmEmailSuccess": "Sikeresen megerősítette e-mail címét! Most már bejelentkezhet.",
|
||||
"totpTitle": "Kétlépcsős hitelesítő kód",
|
||||
@@ -72,21 +72,21 @@
|
||||
"settings": {
|
||||
"title": "Beállítások",
|
||||
"newPasswordTitle": "Jelszó módosítása",
|
||||
"newPassword": "Új jelszó",
|
||||
"newPasswordConfirm": "Új jelszó megerősítése",
|
||||
"currentPassword": "Jelenlegi jelszó",
|
||||
"newPassword": "New password",
|
||||
"newPasswordConfirm": "New password confirmation",
|
||||
"currentPassword": "Current password",
|
||||
"currentPasswordPlaceholder": "Az Ön aktuális jelszava",
|
||||
"passwordsDontMatch": "Az új jelszó és a megerősítés nem egyezik.\nKérlek próbáld újra! Ügyelj a kis és nagy karakterekre!",
|
||||
"passwordUpdateSuccess": "A jelszó módosítása megtörtént.",
|
||||
"updateEmailTitle": "E-mail cím módosítása",
|
||||
"updateEmailNew": "Új e-mail cím",
|
||||
"updateEmailNew": "New email address",
|
||||
"updateEmailSuccess": "E-mail címe sikeresen frissítve. Küldtünk egy linket a megerősítéshez.",
|
||||
"general": {
|
||||
"title": "Általános beállítások",
|
||||
"name": "Név",
|
||||
"newName": "Új név",
|
||||
"name": "My Name",
|
||||
"newName": "The new name",
|
||||
"savedSuccess": "Beállítások sikeresen frissítve.",
|
||||
"emailReminders": "Emlékeztetők küldése a feladatokról E-mailben",
|
||||
"emailReminders": "Send me reminders for tasks via email",
|
||||
"overdueReminders": "Minden nap küldjön nekem egy összefoglalót az elmaradt, késedelmes feladataimról",
|
||||
"discoverableByName": "Engedélyezze a többi felhasználónak, hogy tagként vegyen fel csapatokba vagy projektekbe, amikor a nevemre keresnek",
|
||||
"discoverableByEmail": "Engedélyezze a többi felhasználónak, hogy tagként vegyen fel csapatokhoz vagy projektekhez, amikor a teljes e-mailemet keresik",
|
||||
@@ -95,24 +95,24 @@
|
||||
"weekStartSunday": "Vasárnap",
|
||||
"weekStartMonday": "Hétfő",
|
||||
"language": "Nyelv",
|
||||
"defaultProject": "Alapértelmezett projekt",
|
||||
"defaultView": "Default View",
|
||||
"timezone": "Időzóna",
|
||||
"defaultProject": "Default project",
|
||||
"defaultView": "Default view",
|
||||
"timezone": "Time zone",
|
||||
"overdueTasksRemindersTime": "Esedékes feladatokra emlékeztető e-mail küldésének ideje",
|
||||
"filterUsedOnOverview": "Az áttekintő oldalon használt mentett szűrő"
|
||||
},
|
||||
"totp": {
|
||||
"title": "Kétlépcsős azonosítás",
|
||||
"enroll": "Csatlakozás",
|
||||
"finishSetupPart1": "A beállítás befejezéséhez használja ezt a titkot a TOTP alkalmazásban (Google Authenticator vagy hasonló):",
|
||||
"finishSetupPart1": "To finish your setup, use this secret in your TOTP app (Google Authenticator or similar):",
|
||||
"finishSetupPart2": "Ezután írjon be egy kódot az alkalmazásból alább.",
|
||||
"scanQR": "Alternatív megoldásként beolvashatja ezt a QR-kódot:",
|
||||
"passcode": "Jelkód",
|
||||
"passcodePlaceholder": "A TOTP alkalmazás által generált kód",
|
||||
"passcodePlaceholder": "A code generated by your TOTP application",
|
||||
"setupSuccess": "Két-faktoros azonosítás sikeresen bekapcsolva!",
|
||||
"enterPassword": "Kérjük, írja be jelszavát",
|
||||
"disable": "Kétlépcsős hitelesítés kikapcsolása",
|
||||
"confirmSuccess": "Sikeresen megerősítette a TOTP beállítását és mostantól használhatja!",
|
||||
"confirmSuccess": "You've successfully confirmed your TOTP setup and can use it from now on!",
|
||||
"disableSuccess": "A kétlépcsős azonosítást sikeresen kikapcsoltuk."
|
||||
},
|
||||
"caldav": {
|
||||
@@ -163,6 +163,7 @@
|
||||
"90d": "90 nap",
|
||||
"permissionExplanation": "Az engedélyek lehetővé teszik annak hatókörét, hogy egy API Token mire jogosult.",
|
||||
"titleRequired": "A cím kötelező",
|
||||
"permissionRequired": "Please select at least one permission from the list.",
|
||||
"expired": "Ez a token lejárt {ago}.",
|
||||
"tokenCreatedSuccess": "Íme az új API tokenje: {token}",
|
||||
"tokenCreatedNotSeeAgain": "Tárolja el biztonságos helyen, többé nem fogja látni!",
|
||||
@@ -847,7 +848,8 @@
|
||||
"delete": "Hozzászólás törlése",
|
||||
"deleteText1": "Biztos benne, hogy törölni akarja ezt a hozzászólást?",
|
||||
"deleteSuccess": "A hozzászólás sikeresen törlődött.",
|
||||
"addedSuccess": "A hozzászólás sikeresen hozzáadva."
|
||||
"addedSuccess": "A hozzászólás sikeresen hozzáadva.",
|
||||
"permalink": "Copy permalink to this comment"
|
||||
},
|
||||
"deferDueDate": {
|
||||
"title": "A határidő elhalasztása",
|
||||
@@ -1141,9 +1143,9 @@
|
||||
"1012": "A felhasználó e-mail címe nincs megerősítve.",
|
||||
"1013": "Az új jelszó üres.",
|
||||
"1014": "A régi jelszó üres.",
|
||||
"1015": "A 2FA már engedélyezve van ennél a felhasználónál.",
|
||||
"1016": "A 2FA nincs engedélyezve ennél a felhasználónál.",
|
||||
"1017": "A TOTP jelszó érvénytelen.",
|
||||
"1015": "TOTP is already enabled for this user.",
|
||||
"1016": "TOTP is not enabled for this user.",
|
||||
"1017": "The TOTP passcode is invalid.",
|
||||
"1018": "A felhasználói avatar típus beállítása érvénytelen.",
|
||||
"2001": "Az azonosító nem lehet üres vagy 0.",
|
||||
"2002": "A kérés egyes adatai érvénytelenek voltak.",
|
||||
@@ -1153,8 +1155,8 @@
|
||||
"3006": "A projektmegosztás nem létezik.",
|
||||
"3007": "Ezzel az azonosítóval már létezik projekt.",
|
||||
"3008": "A projekt archiválva van, ezért csak olvasható. Ez a projekthez kapcsolódó összes feladatra is igaz.",
|
||||
"4001": "A projektfeladat szövege nem lehet üres.",
|
||||
"4002": "A projektfeladat nem létezik.",
|
||||
"4001": "The task title cannot be empty.",
|
||||
"4002": "The task does not exist.",
|
||||
"4003": "Minden tömeges szerkesztési feladatnak ugyanahhoz a projekthez kell tartoznia.",
|
||||
"4004": "A feladatok tömeges szerkesztéséhez legalább egy feladatra van szükség.",
|
||||
"4005": "Nincs joga a feladat megtekintéséhez.",
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
"forgotPassword": "Password dimenticata?",
|
||||
"resetPassword": "Reimposta la tua password",
|
||||
"resetPasswordAction": "Inviami il link per reimpostare la password",
|
||||
"resetPasswordSuccess": "Controlla la tua casella di posta! Dovresti avere un'e-mail con le istruzioni su come reimpostare la password.",
|
||||
"resetPasswordSuccess": "Check your inbox! You should have an email with instructions on how to reset your password.",
|
||||
"passwordsDontMatch": "Le password non corrispondono",
|
||||
"confirmEmailSuccess": "Hai confermato con successo la tua e-mail! Ora puoi accedere.",
|
||||
"totpTitle": "Codice di autenticazione a due fattori",
|
||||
@@ -72,21 +72,21 @@
|
||||
"settings": {
|
||||
"title": "Impostazioni",
|
||||
"newPasswordTitle": "Aggiorna la tua Password",
|
||||
"newPassword": "Nuova Password",
|
||||
"newPasswordConfirm": "Conferma della nuova password",
|
||||
"currentPassword": "Password attuale",
|
||||
"newPassword": "New password",
|
||||
"newPasswordConfirm": "New password confirmation",
|
||||
"currentPassword": "Current password",
|
||||
"currentPasswordPlaceholder": "La tua password attuale",
|
||||
"passwordsDontMatch": "La nuova password e la conferma non coincidono.",
|
||||
"passwordUpdateSuccess": "Password aggiornata con successo.",
|
||||
"updateEmailTitle": "Aggiorna l'indirizzo e-mail",
|
||||
"updateEmailNew": "Nuovo indirizzo e-mail",
|
||||
"updateEmailNew": "New email address",
|
||||
"updateEmailSuccess": "Il tuo indirizzo e-mail è stato aggiornato correttamente. Ti abbiamo inviato un collegamento per confermarlo.",
|
||||
"general": {
|
||||
"title": "Impostazioni Generali",
|
||||
"name": "Nome",
|
||||
"newName": "Il nuovo nome",
|
||||
"name": "My Name",
|
||||
"newName": "The new name",
|
||||
"savedSuccess": "Impostazioni salvate con successo.",
|
||||
"emailReminders": "Inviami promemoria per le attività via e-mail",
|
||||
"emailReminders": "Send me reminders for tasks via email",
|
||||
"overdueReminders": "Inviami ogni giorno un riassunto delle attività in ritardo",
|
||||
"discoverableByName": "Allow other users to add me as a member to teams or projects when they search for my name",
|
||||
"discoverableByEmail": "Allow other users to add me as a member to teams or projects when they search for my full email",
|
||||
@@ -95,24 +95,24 @@
|
||||
"weekStartSunday": "Domenica",
|
||||
"weekStartMonday": "Lunedì",
|
||||
"language": "Lingua",
|
||||
"defaultProject": "Progetto Predefinito",
|
||||
"defaultView": "Default View",
|
||||
"timezone": "Fuso Orario",
|
||||
"defaultProject": "Default project",
|
||||
"defaultView": "Default view",
|
||||
"timezone": "Time zone",
|
||||
"overdueTasksRemindersTime": "Orario email attività in scadute",
|
||||
"filterUsedOnOverview": "Saved filter used on the overview page"
|
||||
},
|
||||
"totp": {
|
||||
"title": "Autenticazione a due fattori",
|
||||
"enroll": "Attiva",
|
||||
"finishSetupPart1": "Per completare la configurazione, usa questo codice segreto nella tua app TOTP (Autenticatore Google o simile):",
|
||||
"finishSetupPart1": "To finish your setup, use this secret in your TOTP app (Google Authenticator or similar):",
|
||||
"finishSetupPart2": "Dopodiché, inserisci un codice dalla tua app di qui sotto.",
|
||||
"scanQR": "In alternativa puoi scansionare questo codice QR:",
|
||||
"passcode": "Codice di accesso",
|
||||
"passcodePlaceholder": "Un codice generato dalla tua applicazione TOTP",
|
||||
"passcodePlaceholder": "A code generated by your TOTP application",
|
||||
"setupSuccess": "Autenticazione a due fattori abilitata!",
|
||||
"enterPassword": "Inserisci La Tua Password",
|
||||
"disable": "Disabilita l'autenticazione a due fattori",
|
||||
"confirmSuccess": "Hai confermato con successo la tua configurazione totp e puoi usarla da ora in poi!",
|
||||
"confirmSuccess": "You've successfully confirmed your TOTP setup and can use it from now on!",
|
||||
"disableSuccess": "Autenticazione a due fattori disabilitata."
|
||||
},
|
||||
"caldav": {
|
||||
@@ -163,6 +163,7 @@
|
||||
"90d": "90 Days",
|
||||
"permissionExplanation": "Permissions allow you to scope what an api token is allowed to do.",
|
||||
"titleRequired": "The title is required",
|
||||
"permissionRequired": "Please select at least one permission from the list.",
|
||||
"expired": "This token has expired {ago}.",
|
||||
"tokenCreatedSuccess": "Here is your new api token: {token}",
|
||||
"tokenCreatedNotSeeAgain": "Store it in a secure location, you won't see it again!",
|
||||
@@ -847,7 +848,8 @@
|
||||
"delete": "Elimina questo commento",
|
||||
"deleteText1": "Sei sicuro di voler eliminare questo commento?",
|
||||
"deleteSuccess": "Commento cancellato con successo.",
|
||||
"addedSuccess": "Il commento è stato aggiunto correttamente."
|
||||
"addedSuccess": "Il commento è stato aggiunto correttamente.",
|
||||
"permalink": "Copy permalink to this comment"
|
||||
},
|
||||
"deferDueDate": {
|
||||
"title": "Rinvia data di scadenza",
|
||||
@@ -1141,9 +1143,9 @@
|
||||
"1012": "Indirizzo e-mail dell'utente non confermato.",
|
||||
"1013": "La nuova password è vuota.",
|
||||
"1014": "La vecchia password è vuota.",
|
||||
"1015": "Autenticazione TOTP già abilitata per questo utente.",
|
||||
"1016": "Autenticazione TOTP non abilitata per questo utente.",
|
||||
"1017": "Codice TOTP non valido.",
|
||||
"1015": "TOTP is already enabled for this user.",
|
||||
"1016": "TOTP is not enabled for this user.",
|
||||
"1017": "The TOTP passcode is invalid.",
|
||||
"1018": "L'impostazione del tipo di avatar utente non è valida.",
|
||||
"2001": "L'ID non può essere vuoto o 0.",
|
||||
"2002": "Alcuni dati della richiesta non erano validi.",
|
||||
@@ -1153,8 +1155,8 @@
|
||||
"3006": "The project share does not exist.",
|
||||
"3007": "A project with this identifier already exists.",
|
||||
"3008": "The project is archived and can therefore only be accessed read only. This is also true for all tasks associated with this project.",
|
||||
"4001": "The project task text cannot be empty.",
|
||||
"4002": "The project task does not exist.",
|
||||
"4001": "The task title cannot be empty.",
|
||||
"4002": "The task does not exist.",
|
||||
"4003": "All bulk editing tasks must belong to the same project.",
|
||||
"4004": "Hai bisogno di almeno un'attività quando si modificano in blocco le attività.",
|
||||
"4005": "Non hai il permesso di vedere l'attività.",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user