Compare commits
372 Commits
d014174011
...
8eba71d448
Author | SHA1 | Date |
---|---|---|
Dominik Pschenitschni | 8eba71d448 | |
Dominik Pschenitschni | da54bb92fe | |
Dominik Pschenitschni | 8b7f95135b | |
kolaente | 8909e43410 | |
kolaente | a53ca6a201 | |
kolaente | 99da686a70 | |
kolaente | cf714f09ff | |
kolaente | eb196a8260 | |
kolaente | e28d2aab43 | |
kolaente | b7b2a9ff16 | |
kolaente | d7ca43d6f8 | |
kolaente | 36d0c2af20 | |
kolaente | 11a83dd42c | |
kolaente | ddbbf60f49 | |
kolaente | 1bb9d9ae3f | |
kolaente | 727cf79005 | |
kolaente | e12691998e | |
kolaente | 4cfbdd51f7 | |
kolaente | d2ffc58088 | |
kolaente | dad399dce2 | |
kolaente | 69c5e8bd1c | |
kolaente | 7878e3f30f | |
konrad | 4cff3ebee1 | |
kolaente | 53787a65df | |
kolaente | dfed1f438a | |
kolaente | ad8ca462cb | |
kolaente | 2e537f6d63 | |
renovate | 4bf016f53e | |
renovate | deff90bd8f | |
renovate | 5dbdfd3cd5 | |
renovate | 2719e93cf1 | |
renovate | 756104c7b5 | |
renovate | ddec4bd979 | |
renovate | d14d2405d6 | |
renovate | 753e03dd46 | |
renovate | cd9928036d | |
renovate | e29b191fbe | |
renovate | e8d8767d31 | |
renovate | 2966e0846a | |
renovate | 7870247ad1 | |
renovate | 1f769ae3ab | |
renovate | c0ecde5029 | |
renovate | 94be991ff6 | |
renovate | 5a27973a23 | |
drone | e494af094d | |
renovate | ae643c49d2 | |
renovate | ab569760ef | |
renovate | 614f15cf4a | |
renovate | 4b1003e5e4 | |
renovate | 78c6d6bb1d | |
renovate | 5a214ce22c | |
renovate | 3f68121750 | |
drone | f3294ce755 | |
renovate | 897ba39037 | |
renovate | 0c12c4ebd6 | |
renovate | 6be67cfb98 | |
renovate | 160c099ff8 | |
renovate | 5d7de25a92 | |
konrad | 9b09fadbd0 | |
renovate | 75c68aa03e | |
renovate | c49d582a03 | |
kolaente | 46050611d8 | |
kolaente | a055a3ea52 | |
kolaente | b7a976a9cf | |
kolaente | 42c0fc6185 | |
kolaente | f9b7e2fd76 | |
kolaente | 574ecff12d | |
kolaente | 0af6d79eff | |
konrad | 3639498b3f | |
konrad | 7f56a3537c | |
adrinux | a8fe2cdcbd | |
renovate | c8fa0cc6b1 | |
renovate | fcc096dcf7 | |
renovate | d696d16139 | |
renovate | 81efe27d50 | |
renovate | bd52ac71aa | |
renovate | b949c9f832 | |
renovate | 1f4a016075 | |
renovate | 94c0f6d00f | |
renovate | 06f0597b13 | |
renovate | bfe28787c3 | |
renovate | 8e6aa35f89 | |
renovate | 25ca3e5eaf | |
renovate | 188eb02e07 | |
renovate | 3605d61a17 | |
kolaente | 87ac22b448 | |
kolaente | 8f650316dc | |
kolaente | d7b1d7da7f | |
kolaente | 63e04f874a | |
renovate | 09f6c0d43e | |
renovate | 2b6cbe8006 | |
renovate | 501a720b7c | |
renovate | a72329599b | |
renovate | 50871beeee | |
renovate | 51cafb9d56 | |
renovate | 73ff2d4407 | |
renovate | 3f4bb1b64b | |
renovate | 6a5f5f81a8 | |
renovate | c90f87f74a | |
renovate | a8da12a894 | |
renovate | ae6c982755 | |
drone | 8f367921cf | |
renovate | ea9e84b4d9 | |
renovate | 8aeaa322fe | |
renovate | 83a91e9a11 | |
renovate | 5e6bce8b7e | |
renovate | 574b000b19 | |
renovate | 2c929c1f91 | |
renovate | c41f3edc99 | |
renovate | 9606edbb39 | |
renovate | 360b4da7d3 | |
renovate | 7564c26201 | |
renovate | 6432d18c1b | |
renovate | b57540a8ff | |
renovate | 4c43c87bfe | |
renovate | 83b1333ee5 | |
renovate | eadfc3442f | |
renovate | 14a7676fe4 | |
renovate | 148046f7b8 | |
renovate | 75ec8889a9 | |
renovate | 2292be3ff3 | |
renovate | c2ee125ba2 | |
renovate | df53dc76ce | |
renovate | 1d9fef9172 | |
renovate | 19b4fc20a1 | |
renovate | d1e8931600 | |
renovate | 2dc888790f | |
renovate | 811d62eb52 | |
renovate | 83159b44d0 | |
renovate | b9c677e871 | |
renovate | 4fab17f798 | |
renovate | e95705f45f | |
renovate | 562f84d962 | |
renovate | d29c7db4b6 | |
renovate | d0a2b248e0 | |
renovate | f36c7783ee | |
drone | 42fa8bda71 | |
renovate | 2786f7a7bc | |
renovate | a534229bf5 | |
renovate | 89ed8913fd | |
renovate | 5328c3d6b0 | |
renovate | 6a3290b052 | |
renovate | 8ca64e9e1d | |
renovate | 276fc7a875 | |
renovate | 40e5945d5f | |
renovate | 1fd1645c04 | |
renovate | e96532d59f | |
renovate | c6b3a39a84 | |
renovate | 675d45cf9c | |
renovate | 1fd5ab8d98 | |
renovate | e19b338335 | |
renovate | 50c8077d7e | |
renovate | 4136ed2f96 | |
renovate | 4d362c0462 | |
renovate | c82d2d4c2d | |
renovate | 9622096115 | |
renovate | 9d6aed1a70 | |
renovate | 91a537c379 | |
renovate | 59a5b7021c | |
kolaente | 81993cc2e6 | |
renovate | 666d076801 | |
renovate | 880976f960 | |
renovate | db648a458a | |
drone | 9b67f5627e | |
renovate | d1bf513e39 | |
renovate | e2af71f577 | |
renovate | 210e782c6a | |
renovate | 08b087f590 | |
renovate | 280ada0e2f | |
drone | 913ec95982 | |
renovate | 2ec7856cfe | |
kolaente | cf5460d298 | |
kolaente | f4b0e68322 | |
renovate | 8849e5bee1 | |
renovate | 690c696d13 | |
renovate | 22d10a3f6b | |
renovate | 98f86b91a2 | |
renovate | a9160d223d | |
renovate | 3d15d6e3d4 | |
renovate | 28fdc98d1d | |
renovate | e1a16ac5af | |
renovate | b171cfbf67 | |
renovate | ba0c0342fd | |
renovate | 1081e7ea50 | |
renovate | ae51c44a5e | |
renovate | 7be75c998c | |
renovate | 600fba6b71 | |
renovate | 4aad36333d | |
renovate | e901aef521 | |
kolaente | 7f2189b455 | |
kolaente | db47c1f10c | |
kolaente | a8ee54d626 | |
kolaente | b144802203 | |
kolaente | a23b4a96ee | |
renovate | fc0e3e0d51 | |
renovate | fd857d3db3 | |
kolaente | c06cc6ad7a | |
kolaente | 553fd54f78 | |
kolaente | 74ab197dc6 | |
kolaente | 5b509da215 | |
kolaente | f54f533700 | |
kolaente | 6d0cbc51f6 | |
kolaente | 17ba56f12d | |
kolaente | d5f0158b04 | |
kolaente | 051dd98ff7 | |
kolaente | 7b62a0895d | |
kolaente | 45c05296a6 | |
kolaente | 1256c37b69 | |
kolaente | 9a55482681 | |
kolaente | b83cec2f0e | |
kolaente | 731506fab7 | |
kolaente | 8cdcfaf071 | |
renovate | 42303e37df | |
renovate | 683a68f3ef | |
renovate | f2a0a444b5 | |
renovate | 4dd914af13 | |
renovate | c7a35fa251 | |
renovate | e5e546bc0f | |
renovate | bdad9dadc8 | |
renovate | 46cc8e0cec | |
renovate | d8a60779ef | |
renovate | 52948864ad | |
renovate | eb505bbc25 | |
Dominik Pschenitschni | 9f5e68a125 | |
drone | 9bb73d9f64 | |
renovate | 1a50eb100c | |
renovate | 7e787fd99d | |
renovate | 3aec819e46 | |
renovate | 64d9de2aa6 | |
renovate | 29675670c4 | |
drone | b512f4a6b8 | |
renovate | 6d6220627c | |
renovate | e855728a3e | |
renovate | 15a966cf0d | |
renovate | 54d8ea443d | |
renovate | 213ddc0acb | |
renovate | f6bb2d1aee | |
drone | 76566d3941 | |
konrad | bdb53ec8ee | |
kolaente | da162d5652 | |
kolaente | c7943ef823 | |
kolaente | 622f08fb1b | |
kolaente | 4ce9ac9c66 | |
kolaente | 95d8cdffe4 | |
kolaente | 564f669ed4 | |
kolaente | 74766ce1d0 | |
kolaente | 18f5f8da7d | |
kolaente | 4195953696 | |
kolaente | a6480cdb75 | |
kolaente | 0b6a74d11e | |
renovate | f9309c30b0 | |
Dominik Pschenitschni | 60f58af41a | |
drone | 15d681122a | |
renovate | 90d184f7ad | |
renovate | e14cfd985c | |
drone | 894792b207 | |
renovate | 6d66ee788c | |
Dominik Pschenitschni | c419062e49 | |
Dominik Pschenitschni | 8df73c973b | |
renovate | 026ee3e8da | |
Dominik Pschenitschni | 8ea9d7541f | |
renovate | c0670e6bfc | |
renovate | 3cda4c8478 | |
renovate | e895c8d5d9 | |
renovate | bde0b252bc | |
renovate | 1b46bc9c47 | |
Dominik Pschenitschni | 98cb14a86c | |
renovate | 95390390a7 | |
renovate | 11d450d651 | |
renovate | 0f3ba86efd | |
renovate | 91e92ef772 | |
renovate | 9a46540ebc | |
renovate | 64027d1ee0 | |
renovate | fff113d418 | |
renovate | fc8d2d1a57 | |
renovate | 6a959b2e53 | |
renovate | 56f48cab31 | |
renovate | cd3244e0f9 | |
renovate | b3f207eaff | |
renovate | 34990946f6 | |
renovate | a557a0085d | |
renovate | 762f21188d | |
renovate | a7a3dd7872 | |
renovate | e9498b200d | |
renovate | cd15a756d7 | |
renovate | 2c6ba1fa89 | |
renovate | 51a93b5fd2 | |
renovate | d4061d1b10 | |
renovate | 7f9ba0c727 | |
renovate | 7020c99a7a | |
renovate | befa2d4004 | |
saibotk | 28af46bcd3 | |
Dominik Pschenitschni | 17dc276971 | |
Dominik Pschenitschni | ede8bc5015 | |
Dominik Pschenitschni | 924359f739 | |
Dominik Pschenitschni | b84fe4c88b | |
Dominik Pschenitschni | d57c9af332 | |
Dominik Pschenitschni | eac07d3169 | |
Dominik Pschenitschni | 72d6701404 | |
Dominik Pschenitschni | badbae0e9a | |
kolaente | 4ac7d6b9df | |
kolaente | 7cd89b7bf1 | |
kolaente | a22792a4b4 | |
kolaente | b1ec5b58ee | |
kolaente | 77bf347155 | |
kolaente | 204e94aa74 | |
kolaente | aac777e286 | |
kolaente | 84f177c80e | |
kolaente | 1e4ef96150 | |
kolaente | 4268eee1f2 | |
kolaente | 436c0416d7 | |
kolaente | a78ca6fad3 | |
kolaente | 356b291a57 | |
kolaente | 60be8b428e | |
kolaente | f435ca99f4 | |
kolaente | eefe6bd413 | |
kolaente | 204136266f | |
kolaente | 18f7adf420 | |
kolaente | c5d598cac4 | |
kolaente | eeee1c842a | |
kolaente | 6d6f2b4e33 | |
kolaente | cbbcb7ef23 | |
kolaente | bcd34efe91 | |
kolaente | 6c0d091e36 | |
kolaente | 1abd36ef6e | |
kolaente | 9e7c258347 | |
kolaente | 7135288800 | |
kolaente | 7aa2cfc8d4 | |
kolaente | 3a12be505d | |
kolaente | a74fc47335 | |
kolaente | 0ae8a0e6ef | |
kolaente | e7fa1d3383 | |
kolaente | 6c55411f71 | |
kolaente | 4d23fae9ad | |
kolaente | 16f48bcc2d | |
kolaente | 1e46849c78 | |
kolaente | 8d5bfbe828 | |
kolaente | dabe87af4b | |
kolaente | 6667df5f1f | |
kolaente | 32bdf16892 | |
kolaente | 210a78be86 | |
kolaente | ecf679d8e1 | |
kolaente | 43e83350bd | |
kolaente | c41397f5db | |
kolaente | ccd8602bfd | |
kolaente | 4e8a03066e | |
kolaente | 8d13b979ec | |
kolaente | d272eb2a7a | |
kolaente | 01323a1b45 | |
kolaente | 7dddfea79e | |
kolaente | 1648bcdb70 | |
kolaente | 0710cea9e5 | |
kolaente | 294e89b6f7 | |
kolaente | 75cbc73b33 | |
kolaente | 950fdce111 | |
kolaente | 932f1774ec | |
kolaente | d825960836 | |
kolaente | f691e96e22 | |
kolaente | 0d6ef8f18a | |
kolaente | d6dd1fc0e3 | |
kolaente | 729aa7d4cc | |
kolaente | c24b8af00d | |
kolaente | 8f8d25ece1 | |
kolaente | e93be0d04c | |
kolaente | a5b23a7048 | |
kolaente | 858e7d60a6 | |
kolaente | 12317c56b3 | |
kolaente | 378f782d44 | |
kolaente | b274a796d4 | |
kolaente | 3d1c1e41c7 | |
kolaente | 8115563d67 | |
kolaente | 7408c37dec |
|
@ -120,10 +120,9 @@ steps:
|
|||
from_secret: cypress_project_key
|
||||
commands:
|
||||
- sed -i 's/localhost/api/g' dist/index.html
|
||||
- yarn serve:dist & npx wait-on http://localhost:5000
|
||||
- yarn serve:dist & npx wait-on http://localhost:4173
|
||||
- yarn test:frontend --browser chrome --record
|
||||
depends_on:
|
||||
- dependencies
|
||||
- build-prod
|
||||
|
||||
- name: deploy-preview
|
||||
|
@ -137,6 +136,9 @@ steps:
|
|||
GITEA_TOKEN:
|
||||
from_secret: gitea_token
|
||||
commands:
|
||||
- cp -r dist dist-preview
|
||||
# Override the default api url used for preview
|
||||
- sed -i 's|localhost:3456|try.vikunja.io|g' dist-preview/index.html
|
||||
- shasum -a 384 -c ./scripts/deploy-preview-netlify.js.sha384
|
||||
- node ./scripts/deploy-preview-netlify.js
|
||||
depends_on:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"baseUrl": "http://localhost:5000",
|
||||
"baseUrl": "http://localhost:4173",
|
||||
"env": {
|
||||
"API_URL": "http://localhost:3456/api/v1",
|
||||
"TEST_SECRET": "averyLongSecretToSe33dtheDB"
|
||||
|
|
|
@ -176,7 +176,7 @@ describe('List View Kanban', () => {
|
|||
.click()
|
||||
|
||||
cy.get('.task-view .action-buttons .button', { timeout: 3000 })
|
||||
.contains('Move task')
|
||||
.contains('Move')
|
||||
.click()
|
||||
cy.get('.task-view .content.details .field .multiselect.control .input-wrapper input')
|
||||
.type(`${lists[1].title}{enter}`)
|
||||
|
|
|
@ -72,7 +72,7 @@ describe('Lists', () => {
|
|||
.should('contain', newListName)
|
||||
.should('not.contain', lists[0].title)
|
||||
cy.visit('/')
|
||||
cy.get('.card-content .tasks')
|
||||
cy.get('.card-content')
|
||||
.should('contain', newListName)
|
||||
.should('not.contain', lists[0].title)
|
||||
})
|
||||
|
|
|
@ -210,7 +210,7 @@ describe('Task', () => {
|
|||
cy.visit(`/tasks/${tasks[0].id}`)
|
||||
|
||||
cy.get('.task-view .action-buttons .button')
|
||||
.contains('Move task')
|
||||
.contains('Move')
|
||||
.click()
|
||||
cy.get('.task-view .content.details .field .multiselect.control .input-wrapper input')
|
||||
.type(`${lists[1].title}{enter}`)
|
||||
|
@ -237,7 +237,7 @@ describe('Task', () => {
|
|||
|
||||
cy.get('.task-view .action-buttons .button')
|
||||
.should('be.visible')
|
||||
.contains('Delete task')
|
||||
.contains('Delete')
|
||||
.click()
|
||||
cy.get('.modal-mask .modal-container .modal-content .header')
|
||||
.should('contain', 'Delete this task')
|
||||
|
@ -317,7 +317,7 @@ describe('Task', () => {
|
|||
cy.visit(`/tasks/${tasks[0].id}`)
|
||||
|
||||
cy.get('.task-view .action-buttons .button')
|
||||
.contains('Add labels')
|
||||
.contains('Add Labels')
|
||||
.should('be.visible')
|
||||
.click()
|
||||
cy.get('.task-view .details.labels-list .multiselect input')
|
||||
|
@ -344,7 +344,7 @@ describe('Task', () => {
|
|||
cy.visit(`/tasks/${tasks[0].id}`)
|
||||
|
||||
cy.get('.task-view .action-buttons .button')
|
||||
.contains('Add labels')
|
||||
.contains('Add Labels')
|
||||
.click()
|
||||
cy.get('.task-view .details.labels-list .multiselect input')
|
||||
.type(labels[0].title)
|
||||
|
|
|
@ -6,7 +6,7 @@ describe('Log out', () => {
|
|||
|
||||
cy.get('.navbar .user .username')
|
||||
.click()
|
||||
cy.get('.navbar .user .dropdown-menu a.dropdown-item')
|
||||
cy.get('.navbar .user .dropdown-menu .dropdown-item')
|
||||
.contains('Logout')
|
||||
.click()
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[build]
|
||||
command = "yarn build"
|
||||
publish = "dist"
|
||||
publish = "dist-preview"
|
||||
|
||||
[[redirects]]
|
||||
from = "/*"
|
||||
|
|
96
package.json
96
package.json
|
@ -5,7 +5,7 @@
|
|||
"scripts": {
|
||||
"serve": "vite",
|
||||
"serve:dist-dev": "node scripts/serve-dist.js",
|
||||
"serve:dist": "vite preview",
|
||||
"serve:dist": "vite preview --port 4173",
|
||||
"build": "vite build && workbox copyLibraries dist/",
|
||||
"build:modern-only": "BUILD_MODERN_ONLY=true vite build && workbox copyLibraries dist/",
|
||||
"build:dev": "vite build -m development --outDir dist-dev/",
|
||||
|
@ -20,78 +20,78 @@
|
|||
"dependencies": {
|
||||
"@github/hotkey": "2.0.0",
|
||||
"@kyvg/vue3-notification": "2.3.4",
|
||||
"@sentry/tracing": "6.17.7",
|
||||
"@sentry/vue": "6.17.7",
|
||||
"@sentry/tracing": "6.19.3",
|
||||
"@sentry/vue": "6.19.3",
|
||||
"@types/is-touch-device": "1.0.0",
|
||||
"@vue/compat": "3.2.30",
|
||||
"@vueuse/core": "7.6.0",
|
||||
"@vueuse/router": "7.6.1",
|
||||
"@vue/compat": "3.2.31",
|
||||
"@vueuse/core": "8.2.3",
|
||||
"@vueuse/router": "8.2.3",
|
||||
"blurhash": "^1.1.4",
|
||||
"bulma-css-variables": "0.9.33",
|
||||
"camel-case": "4.1.2",
|
||||
"codemirror": "5.65.1",
|
||||
"codemirror": "5.65.2",
|
||||
"copy-to-clipboard": "3.3.1",
|
||||
"date-fns": "2.28.0",
|
||||
"dompurify": "2.3.5",
|
||||
"dompurify": "2.3.6",
|
||||
"easymde": "2.16.1",
|
||||
"flatpickr": "4.6.9",
|
||||
"flatpickr": "4.6.11",
|
||||
"flexsearch": "0.7.21",
|
||||
"highlight.js": "11.4.0",
|
||||
"highlight.js": "11.5.0",
|
||||
"is-touch-device": "1.0.1",
|
||||
"lodash.clonedeep": "4.5.0",
|
||||
"lodash.debounce": "4.0.8",
|
||||
"marked": "4.0.12",
|
||||
"register-service-worker": "1.7.2",
|
||||
"snake-case": "3.0.4",
|
||||
"ufo": "0.7.10",
|
||||
"ufo": "0.8.3",
|
||||
"v-tooltip": "4.0.0-beta.17",
|
||||
"vue": "3.2.30",
|
||||
"vue-advanced-cropper": "2.8.0",
|
||||
"vue": "3.2.31",
|
||||
"vue-advanced-cropper": "2.8.1",
|
||||
"vue-drag-resize": "2.0.3",
|
||||
"vue-flatpickr-component": "9.0.5",
|
||||
"vue-i18n": "9.2.0-beta.30",
|
||||
"vue-router": "4.0.12",
|
||||
"vue-router": "4.0.14",
|
||||
"vuedraggable": "4.1.0",
|
||||
"vuex": "4.0.2",
|
||||
"workbox-precaching": "6.4.2"
|
||||
"workbox-precaching": "6.5.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@4tw/cypress-drag-drop": "2.1.0",
|
||||
"@faker-js/faker": "6.0.0-alpha.6",
|
||||
"@fortawesome/fontawesome-svg-core": "1.3.0",
|
||||
"@fortawesome/free-regular-svg-icons": "5.15.4",
|
||||
"@fortawesome/free-solid-svg-icons": "5.15.4",
|
||||
"@faker-js/faker": "6.1.1",
|
||||
"@fortawesome/fontawesome-svg-core": "6.1.1",
|
||||
"@fortawesome/free-regular-svg-icons": "6.1.1",
|
||||
"@fortawesome/free-solid-svg-icons": "6.1.1",
|
||||
"@fortawesome/vue-fontawesome": "3.0.0-5",
|
||||
"@types/flexsearch": "0.7.2",
|
||||
"@typescript-eslint/eslint-plugin": "5.11.0",
|
||||
"@typescript-eslint/parser": "5.11.0",
|
||||
"@vitejs/plugin-legacy": "1.7.1",
|
||||
"@vitejs/plugin-vue": "2.2.0",
|
||||
"@types/flexsearch": "0.7.3",
|
||||
"@typescript-eslint/eslint-plugin": "5.17.0",
|
||||
"@typescript-eslint/parser": "5.17.0",
|
||||
"@vitejs/plugin-legacy": "1.8.0",
|
||||
"@vitejs/plugin-vue": "2.3.1",
|
||||
"@vue/eslint-config-typescript": "10.0.0",
|
||||
"autoprefixer": "10.4.2",
|
||||
"axios": "0.25.0",
|
||||
"browserslist": "4.19.1",
|
||||
"caniuse-lite": "1.0.30001311",
|
||||
"cypress": "9.4.1",
|
||||
"esbuild": "0.14.21",
|
||||
"eslint": "8.9.0",
|
||||
"eslint-plugin-vue": "8.4.1",
|
||||
"express": "4.17.2",
|
||||
"happy-dom": "2.31.1",
|
||||
"netlify-cli": "8.16.1",
|
||||
"postcss": "8.4.6",
|
||||
"postcss-preset-env": "7.3.1",
|
||||
"rollup": "2.67.2",
|
||||
"rollup-plugin-visualizer": "5.5.4",
|
||||
"sass": "1.49.7",
|
||||
"slugify": "1.6.5",
|
||||
"typescript": "4.5.5",
|
||||
"vite": "2.7.13",
|
||||
"autoprefixer": "10.4.4",
|
||||
"axios": "0.26.1",
|
||||
"browserslist": "4.20.2",
|
||||
"caniuse-lite": "1.0.30001324",
|
||||
"cypress": "9.5.3",
|
||||
"esbuild": "0.14.30",
|
||||
"eslint": "8.12.0",
|
||||
"eslint-plugin-vue": "8.5.0",
|
||||
"express": "4.17.3",
|
||||
"happy-dom": "2.55.0",
|
||||
"netlify-cli": "9.13.5",
|
||||
"postcss": "8.4.12",
|
||||
"postcss-preset-env": "7.4.3",
|
||||
"rollup": "2.70.1",
|
||||
"rollup-plugin-visualizer": "5.6.0",
|
||||
"sass": "1.49.11",
|
||||
"typescript": "4.6.3",
|
||||
"vite": "2.9.1",
|
||||
"vite-plugin-pwa": "0.11.13",
|
||||
"vite-svg-loader": "3.1.2",
|
||||
"vitest": "0.3.2",
|
||||
"vue-tsc": "0.31.2",
|
||||
"vite-svg-loader": "3.2.0",
|
||||
"vitest": "0.8.2",
|
||||
"vue-tsc": "0.33.9",
|
||||
"wait-on": "6.0.1",
|
||||
"workbox-cli": "6.4.2"
|
||||
"workbox-cli": "6.5.2"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
|
@ -145,5 +145,5 @@
|
|||
}
|
||||
},
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"packageManager": "yarn@1.22.17"
|
||||
"packageManager": "yarn@1.22.18"
|
||||
}
|
||||
|
|
|
@ -1,20 +1,24 @@
|
|||
const slugify = require('slugify')
|
||||
const {exec} = require('child_process')
|
||||
const axios = require('axios')
|
||||
|
||||
const BOT_USER_ID = 513
|
||||
const giteaToken = process.env.GITEA_TOKEN
|
||||
const siteId = process.env.NETLIFY_SITE_ID
|
||||
const branchSlug = slugify(process.env.DRONE_SOURCE_BRANCH)
|
||||
const branchSlug = String(process.env.DRONE_SOURCE_BRANCH)
|
||||
.trim()
|
||||
.normalize('NFKD')
|
||||
.toLowerCase()
|
||||
.replace(/[.\s/]/g, '-')
|
||||
.replace(/[^A-Za-z\d-]/g, '')
|
||||
const prNumber = process.env.DRONE_PULL_REQUEST
|
||||
|
||||
const prIssueCommentsUrl = `https://kolaente.dev/api/v1/repos/vikunja/frontend/issues/${prNumber}/comments`
|
||||
const alias = `${prNumber}-${branchSlug}`
|
||||
const alias = `${prNumber}-${branchSlug}`.substring(0,37)
|
||||
const fullPreviewUrl = `https://${alias}--vikunja-frontend-preview.netlify.app`
|
||||
|
||||
const promiseExec = cmd => {
|
||||
return new Promise((resolve, reject) => {
|
||||
exec(cmd, (error, stdout, stderr) => {
|
||||
exec(cmd, (error, stdout) => {
|
||||
if (error) {
|
||||
reject(error)
|
||||
return
|
||||
|
|
|
@ -1 +1 @@
|
|||
55ce0faaa2c1919341617ccfaeccbb6029ac12107964ff488985cff13dd952f1a991df3ab0d4b0705deb761e508e6434 ./scripts/deploy-preview-netlify.js
|
||||
bb46342a0a08105b340ba7976cff9d80ef89901120ec0639669caa70bb7d2dbc43e78b1f635a7654ab2456e8358c98a4 ./scripts/deploy-preview-netlify.js
|
||||
|
|
|
@ -3,7 +3,7 @@ const express = require('express')
|
|||
const app = express()
|
||||
|
||||
const p = path.join(__dirname, '..', 'dist-dev')
|
||||
const port = 5000
|
||||
const port = 4173
|
||||
|
||||
app.use(express.static(p))
|
||||
// Handle urls set by the frontend
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<ready>
|
||||
<template v-if="authUser">
|
||||
<top-navigation/>
|
||||
<TheNavigation/>
|
||||
<content-auth/>
|
||||
</template>
|
||||
<content-link-share v-else-if="authLinkShare"/>
|
||||
|
@ -27,7 +27,7 @@ import {success} from '@/message'
|
|||
|
||||
import Notification from '@/components/misc/notification.vue'
|
||||
import KeyboardShortcuts from './components/misc/keyboard-shortcuts/index.vue'
|
||||
import TopNavigation from './components/home/topNavigation.vue'
|
||||
import TheNavigation from '@/components/home/TheNavigation.vue'
|
||||
import ContentAuth from './components/home/contentAuth.vue'
|
||||
import ContentLinkShare from './components/home/contentLinkShare.vue'
|
||||
import NoAuthWrapper from '@/components/misc/no-auth-wrapper.vue'
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
export const DATE_RANGES = {
|
||||
// Format:
|
||||
// Key is the title, as a translation string, the first entry of the value array
|
||||
// is the "from" date, the second one is the "to" date.
|
||||
'today': ['now/d', 'now/d+1d'],
|
||||
|
||||
'lastWeek': ['now/w-1w', 'now/w-2w'],
|
||||
'thisWeek': ['now/w', 'now/w+1w'],
|
||||
'restOfThisWeek': ['now', 'now/w+1w'],
|
||||
'nextWeek': ['now/w+1w', 'now/w+2w'],
|
||||
'next7Days': ['now', 'now+7d'],
|
||||
|
||||
'lastMonth': ['now/M-1M', 'now/M-2M'],
|
||||
'thisMonth': ['now/M', 'now/M+1M'],
|
||||
'restOfThisMonth': ['now', 'now/M+1M'],
|
||||
'nextMonth': ['now/M+1M', 'now/M+2M'],
|
||||
'next30Days': ['now', 'now+30d'],
|
||||
|
||||
'thisYear': ['now/y', 'now/y+1y'],
|
||||
'restOfThisYear': ['now', 'now/y+1y'],
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
<template>
|
||||
<card
|
||||
class="has-no-shadow how-it-works-modal"
|
||||
:title="$t('input.datemathHelp.title')">
|
||||
<p>
|
||||
{{ $t('input.datemathHelp.intro') }}
|
||||
</p>
|
||||
<p>
|
||||
<i18n-t keypath="input.datemathHelp.expression">
|
||||
<code>now</code>
|
||||
<code>||</code>
|
||||
</i18n-t>
|
||||
</p>
|
||||
<p>
|
||||
<i18n-t keypath="input.datemathHelp.similar">
|
||||
<BaseButton
|
||||
href="https://grafana.com/docs/grafana/latest/dashboards/time-range-controls/"
|
||||
target="_blank">
|
||||
Grafana
|
||||
</BaseButton>
|
||||
<BaseButton
|
||||
href="https://www.elastic.co/guide/en/elasticsearch/reference/7.3/common-options.html#date-math"
|
||||
target="_blank">
|
||||
Elasticsearch
|
||||
</BaseButton>
|
||||
</i18n-t>
|
||||
</p>
|
||||
<p>{{ $t('misc.forExample') }}</p>
|
||||
<ul>
|
||||
<li><code>+1d</code>{{ $t('input.datemathHelp.add1Day') }}</li>
|
||||
<li><code>-1d</code>{{ $t('input.datemathHelp.minus1Day') }}</li>
|
||||
<li><code>/d</code>{{ $t('input.datemathHelp.roundDay') }}</li>
|
||||
</ul>
|
||||
<p>{{ $t('input.datemathHelp.supportedUnits') }}</p>
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>s</code></td>
|
||||
<td>{{ $t('input.datemathHelp.units.seconds') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>m</code></td>
|
||||
<td>{{ $t('input.datemathHelp.units.minutes') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>h</code></td>
|
||||
<td>{{ $t('input.datemathHelp.units.hours') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>H</code></td>
|
||||
<td>{{ $t('input.datemathHelp.units.hours') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>d</code></td>
|
||||
<td>{{ $t('input.datemathHelp.units.days') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>w</code></td>
|
||||
<td>{{ $t('input.datemathHelp.units.weeks') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>M</code></td>
|
||||
<td>{{ $t('input.datemathHelp.units.months') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>y</code></td>
|
||||
<td>{{ $t('input.datemathHelp.units.years') }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p>{{ $t('input.datemathHelp.someExamples') }}</p>
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>now</code></td>
|
||||
<td>{{ $t('input.datemathHelp.examples.now') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>now+24h</code></td>
|
||||
<td>{{ $t('input.datemathHelp.examples.in24h') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>now/d</code></td>
|
||||
<td>{{ $t('input.datemathHelp.examples.today') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>now/w</code></td>
|
||||
<td>{{ $t('input.datemathHelp.examples.beginningOfThisWeek') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>now/w+1w</code></td>
|
||||
<td>{{ $t('input.datemathHelp.examples.endOfThisWeek') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>now+30d</code></td>
|
||||
<td>{{ $t('input.datemathHelp.examples.in30Days') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>{{ exampleDate }}||+1M/d</code></td>
|
||||
<td>
|
||||
<i18n-t keypath="input.datemathHelp.examples.datePlusMonth">
|
||||
<code>{{ exampleDate }}</code>
|
||||
</i18n-t>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</card>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {format} from 'date-fns'
|
||||
import BaseButton from '@/components/base/BaseButton.vue'
|
||||
|
||||
const exampleDate = format(new Date(), 'yyyy-MM-dd')
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.how-it-works-modal {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
p {
|
||||
display: inline-block !important;
|
||||
}
|
||||
|
||||
.base-button {
|
||||
display: inline;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,254 @@
|
|||
<template>
|
||||
<div class="datepicker-with-range-container">
|
||||
<popup>
|
||||
<template #trigger="{toggle}">
|
||||
<slot name="trigger" :toggle="toggle" :buttonText="buttonText"></slot>
|
||||
</template>
|
||||
<template #content="{isOpen}">
|
||||
<div class="datepicker-with-range" :class="{'is-open': isOpen}">
|
||||
<div class="selections">
|
||||
<BaseButton @click="setDateRange(null)" :class="{'is-active': customRangeActive}">
|
||||
{{ $t('misc.custom') }}
|
||||
</BaseButton>
|
||||
<BaseButton
|
||||
v-for="(value, text) in DATE_RANGES"
|
||||
:key="text"
|
||||
@click="setDateRange(value)"
|
||||
:class="{'is-active': from === value[0] && to === value[1]}">
|
||||
{{ $t(`input.datepickerRange.ranges.${text}`) }}
|
||||
</BaseButton>
|
||||
</div>
|
||||
<div class="flatpickr-container input-group">
|
||||
<label class="label">
|
||||
{{ $t('input.datepickerRange.from') }}
|
||||
<div class="field has-addons">
|
||||
<div class="control is-fullwidth">
|
||||
<input class="input" type="text" v-model="from"/>
|
||||
</div>
|
||||
<div class="control">
|
||||
<x-button icon="calendar" variant="secondary" data-toggle/>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
<label class="label">
|
||||
{{ $t('input.datepickerRange.to') }}
|
||||
<div class="field has-addons">
|
||||
<div class="control is-fullwidth">
|
||||
<input class="input" type="text" v-model="to"/>
|
||||
</div>
|
||||
<div class="control">
|
||||
<x-button icon="calendar" variant="secondary" data-toggle/>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
<flat-pickr
|
||||
:config="flatPickerConfig"
|
||||
v-model="flatpickrRange"
|
||||
/>
|
||||
|
||||
<p>
|
||||
{{ $t('input.datemathHelp.canuse') }}
|
||||
<BaseButton class="has-text-primary" @click="showHowItWorks = true">
|
||||
{{ $t('input.datemathHelp.learnhow') }}
|
||||
</BaseButton>
|
||||
</p>
|
||||
|
||||
<modal
|
||||
@close="() => showHowItWorks = false"
|
||||
:enabled="showHowItWorks"
|
||||
transition-name="fade"
|
||||
:overflow="true"
|
||||
variant="hint-modal"
|
||||
>
|
||||
<DatemathHelp/>
|
||||
</modal>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</popup>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {computed, ref, watch} from 'vue'
|
||||
import {useStore} from 'vuex'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
|
||||
import flatPickr from 'vue-flatpickr-component'
|
||||
import 'flatpickr/dist/flatpickr.css'
|
||||
|
||||
import Popup from '@/components/misc/popup.vue'
|
||||
import {DATE_RANGES} from '@/components/date/dateRanges'
|
||||
import BaseButton from '@/components/base/BaseButton.vue'
|
||||
import DatemathHelp from '@/components/date/datemathHelp.vue'
|
||||
|
||||
const store = useStore()
|
||||
const {t} = useI18n()
|
||||
|
||||
const emit = defineEmits(['dateChanged'])
|
||||
|
||||
// FIXME: This seems to always contain the default value - that breaks the picker
|
||||
const weekStart = computed<number>(() => store.state.auth.settings.weekStart ?? 0)
|
||||
const flatPickerConfig = computed(() => ({
|
||||
altFormat: t('date.altFormatLong'),
|
||||
altInput: true,
|
||||
dateFormat: 'Y-m-d H:i',
|
||||
enableTime: false,
|
||||
wrap: true,
|
||||
mode: 'range',
|
||||
locale: {
|
||||
firstDayOf7Days: weekStart.value,
|
||||
},
|
||||
}))
|
||||
|
||||
const showHowItWorks = ref(false)
|
||||
|
||||
const flatpickrRange = ref('')
|
||||
|
||||
const from = ref('')
|
||||
const to = ref('')
|
||||
|
||||
function emitChanged() {
|
||||
emit('dateChanged', {
|
||||
dateFrom: from.value === '' ? null : from.value,
|
||||
dateTo: to.value === '' ? null : to.value,
|
||||
})
|
||||
}
|
||||
|
||||
watch(
|
||||
() => flatpickrRange.value,
|
||||
(newVal: string | null) => {
|
||||
if (newVal === null) {
|
||||
return
|
||||
}
|
||||
|
||||
const [fromDate, toDate] = newVal.split(' to ')
|
||||
|
||||
if (typeof fromDate === 'undefined' || typeof toDate === 'undefined') {
|
||||
return
|
||||
}
|
||||
|
||||
from.value = fromDate
|
||||
to.value = toDate
|
||||
|
||||
emitChanged()
|
||||
},
|
||||
)
|
||||
watch(() => from.value, emitChanged)
|
||||
watch(() => to.value, emitChanged)
|
||||
|
||||
function setDateRange(range: string[] | null) {
|
||||
if (range === null) {
|
||||
from.value = ''
|
||||
to.value = ''
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
from.value = range[0]
|
||||
to.value = range[1]
|
||||
}
|
||||
|
||||
const customRangeActive = computed<boolean>(() => {
|
||||
return !Object.values(DATE_RANGES).some(range => from.value === range[0] && to.value === range[1])
|
||||
})
|
||||
|
||||
const buttonText = computed<string>(() => {
|
||||
if (from.value !== '' && to.value !== '') {
|
||||
return t('input.datepickerRange.fromto', {
|
||||
from: from.value,
|
||||
to: to.value,
|
||||
})
|
||||
}
|
||||
|
||||
return t('task.show.select')
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.datepicker-with-range-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
:deep(.popup) {
|
||||
z-index: 10;
|
||||
margin-top: 1rem;
|
||||
border-radius: $radius;
|
||||
border: 1px solid var(--grey-200);
|
||||
background-color: var(--white);
|
||||
box-shadow: $shadow;
|
||||
|
||||
&.is-open {
|
||||
width: 500px;
|
||||
height: 320px;
|
||||
}
|
||||
}
|
||||
|
||||
.datepicker-with-range {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
:deep(.flatpickr-calendar) {
|
||||
margin: 0 auto 8px;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.flatpickr-container {
|
||||
width: 70%;
|
||||
border-left: 1px solid var(--grey-200);
|
||||
padding: 1rem;
|
||||
font-size: .9rem;
|
||||
|
||||
// Flatpickr has no option to use it without an input field so we're hiding it instead
|
||||
:deep(input.form-control.input) {
|
||||
height: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.field .control :deep(.button) {
|
||||
border: 1px solid var(--input-border-color);
|
||||
height: 2.25rem;
|
||||
|
||||
&:hover {
|
||||
border: 1px solid var(--input-hover-border-color);
|
||||
}
|
||||
}
|
||||
|
||||
.label, .input, :deep(.button) {
|
||||
font-size: .9rem;
|
||||
}
|
||||
}
|
||||
|
||||
.selections {
|
||||
width: 30%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-top: .5rem;
|
||||
overflow-y: scroll;
|
||||
|
||||
button {
|
||||
display: block;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
padding: .5rem 1rem;
|
||||
transition: $transition;
|
||||
font-size: .9rem;
|
||||
color: var(--text);
|
||||
background: transparent;
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
|
||||
&.is-active {
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
&:hover, &.is-active {
|
||||
background-color: var(--grey-100);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,8 +1,7 @@
|
|||
<template>
|
||||
<button
|
||||
type="button"
|
||||
@click="$store.commit('toggleMenu')"
|
||||
<BaseButton
|
||||
class="menu-show-button"
|
||||
@click="$store.commit('toggleMenu')"
|
||||
@shortkey="() => $store.commit('toggleMenu')"
|
||||
v-shortcut="'Control+e'"
|
||||
:title="$t('keyboardShortcuts.toggleMenu')"
|
||||
|
@ -10,11 +9,14 @@
|
|||
/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
import {computed} from 'vue'
|
||||
import {store} from '@/store'
|
||||
import {useStore} from 'vuex'
|
||||
|
||||
const menuActive = computed(() => store.menuActive)
|
||||
import BaseButton from '@/components/base/BaseButton.vue'
|
||||
|
||||
const store = useStore()
|
||||
const menuActive = computed(() => store.state.menuActive)
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
@ -22,11 +24,6 @@ $lineWidth: 2rem;
|
|||
$size: $lineWidth + 1rem;
|
||||
|
||||
.menu-show-button {
|
||||
// FIXME: create general button component
|
||||
appearance: none;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
|
||||
min-height: $size;
|
||||
width: $size;
|
||||
|
||||
|
|
|
@ -32,12 +32,13 @@
|
|||
</a>
|
||||
<notifications/>
|
||||
<div class="user">
|
||||
<img :src="userAvatar" alt="" class="avatar" width="40" height="40"/>
|
||||
<dropdown class="is-right" ref="usernameDropdown">
|
||||
<template #trigger>
|
||||
<x-button
|
||||
variant="secondary"
|
||||
:shadow="false">
|
||||
:shadow="false"
|
||||
>
|
||||
<img :src="userAvatar" alt="" class="avatar" width="40" height="40"/>
|
||||
<span class="username">{{ userInfo.name !== '' ? userInfo.name : userInfo.username }}</span>
|
||||
<span class="icon is-small">
|
||||
<icon icon="chevron-down"/>
|
||||
|
@ -45,92 +46,96 @@
|
|||
</x-button>
|
||||
</template>
|
||||
|
||||
<router-link :to="{name: 'user.settings'}" class="dropdown-item">
|
||||
<BaseButton
|
||||
:to="{name: 'user.settings'}"
|
||||
class="dropdown-item"
|
||||
>
|
||||
{{ $t('user.settings.title') }}
|
||||
</router-link>
|
||||
<a
|
||||
</BaseButton>
|
||||
<BaseButton
|
||||
v-if="imprintUrl"
|
||||
:href="imprintUrl"
|
||||
class="dropdown-item"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener nofollow"
|
||||
v-if="imprintUrl">
|
||||
>
|
||||
{{ $t('navigation.imprint') }}
|
||||
</a>
|
||||
<a
|
||||
</BaseButton>
|
||||
<BaseButton
|
||||
v-if="privacyPolicyUrl"
|
||||
:href="privacyPolicyUrl"
|
||||
class="dropdown-item"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener nofollow"
|
||||
v-if="privacyPolicyUrl">
|
||||
>
|
||||
{{ $t('navigation.privacy') }}
|
||||
</a>
|
||||
<a @click="$store.commit('keyboardShortcutsActive', true)" class="dropdown-item">
|
||||
</BaseButton>
|
||||
<BaseButton
|
||||
@click="$store.commit('keyboardShortcutsActive', true)"
|
||||
class="dropdown-item"
|
||||
>
|
||||
{{ $t('keyboardShortcuts.title') }}
|
||||
</a>
|
||||
<router-link :to="{name: 'about'}" class="dropdown-item">
|
||||
</BaseButton>
|
||||
<BaseButton
|
||||
:to="{name: 'about'}"
|
||||
class="dropdown-item"
|
||||
>
|
||||
{{ $t('about.title') }}
|
||||
</router-link>
|
||||
<a @click="logout()" class="dropdown-item">
|
||||
</BaseButton>
|
||||
<BaseButton
|
||||
@click="logout()"
|
||||
class="dropdown-item"
|
||||
>
|
||||
{{ $t('user.auth.logout') }}
|
||||
</a>
|
||||
</BaseButton>
|
||||
</dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapState} from 'vuex'
|
||||
import {CURRENT_LIST, QUICK_ACTIONS_ACTIVE} from '@/store/mutation-types'
|
||||
<script setup langs="ts">
|
||||
import {ref, computed, onMounted, nextTick} from 'vue'
|
||||
import {useStore} from 'vuex'
|
||||
import {useRouter} from 'vue-router'
|
||||
|
||||
import {QUICK_ACTIONS_ACTIVE} from '@/store/mutation-types'
|
||||
import Rights from '@/models/constants/rights.json'
|
||||
|
||||
import Update from '@/components/home/update.vue'
|
||||
import ListSettingsDropdown from '@/components/list/list-settings-dropdown.vue'
|
||||
import Dropdown from '@/components/misc/dropdown.vue'
|
||||
import Notifications from '@/components/notifications/notifications.vue'
|
||||
import Logo from '@/components/home/Logo.vue'
|
||||
import BaseButton from '@/components/base/BaseButton.vue'
|
||||
import MenuButton from '@/components/home/MenuButton.vue'
|
||||
|
||||
export default {
|
||||
name: 'topNavigation',
|
||||
components: {
|
||||
Notifications,
|
||||
Dropdown,
|
||||
ListSettingsDropdown,
|
||||
Update,
|
||||
Logo,
|
||||
MenuButton,
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
userInfo: state => state.auth.info,
|
||||
userAvatar: state => state.auth.avatarUrl,
|
||||
userAuthenticated: state => state.auth.authenticated,
|
||||
currentList: CURRENT_LIST,
|
||||
background: 'background',
|
||||
imprintUrl: state => state.config.legal.imprintUrl,
|
||||
privacyPolicyUrl: state => state.config.legal.privacyPolicyUrl,
|
||||
canWriteCurrentList: state => state.currentList.maxRight > Rights.READ,
|
||||
}),
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
if (typeof this.$refs.usernameDropdown === 'undefined' || typeof this.$refs.listTitle === 'undefined') {
|
||||
return
|
||||
}
|
||||
const store = useStore()
|
||||
|
||||
const usernameWidth = this.$refs.usernameDropdown.$el.clientWidth
|
||||
this.$refs.listTitle.style.setProperty('--nav-username-width', `${usernameWidth}px`)
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
logout() {
|
||||
this.$store.dispatch('auth/logout')
|
||||
this.$router.push({name: 'user.login'})
|
||||
},
|
||||
openQuickActions() {
|
||||
this.$store.commit(QUICK_ACTIONS_ACTIVE, true)
|
||||
},
|
||||
},
|
||||
const userInfo = computed(() => store.state.auth.info)
|
||||
const userAvatar = computed(() => store.state.auth.avatarUrl)
|
||||
const currentList = computed(() => store.state.currentList)
|
||||
const background = computed(() => store.state.background)
|
||||
const imprintUrl = computed(() => store.state.config.legal.imprintUrl)
|
||||
const privacyPolicyUrl = computed(() => store.state.config.legal.privacyPolicyUrl)
|
||||
const canWriteCurrentList = computed(() => store.state.currentList.maxRight > Rights.READ)
|
||||
|
||||
const usernameDropdown = ref()
|
||||
const listTitle = ref()
|
||||
onMounted(async () => {
|
||||
await nextTick()
|
||||
if (typeof usernameDropdown.value === 'undefined' || typeof listTitle.value === 'undefined') {
|
||||
return
|
||||
}
|
||||
|
||||
const usernameWidth = usernameDropdown.value.$el.clientWidth
|
||||
listTitle.value.style.setProperty('--nav-username-width', `${usernameWidth}px`)
|
||||
})
|
||||
|
||||
const router = useRouter()
|
||||
function logout() {
|
||||
store.dispatch('auth/logout')
|
||||
router.push({name: 'user.login'})
|
||||
}
|
||||
|
||||
function openQuickActions() {
|
||||
store.commit(QUICK_ACTIONS_ACTIVE, true)
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -246,6 +251,7 @@ $hamburger-menu-icon-width: 28px;
|
|||
border-radius: 100%;
|
||||
vertical-align: middle;
|
||||
height: 40px;
|
||||
margin-right: var(--button-padding-horizontal);
|
||||
}
|
||||
|
||||
:deep(.dropdown-trigger .button) {
|
|
@ -1,17 +1,21 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="content-auth" :class="{'modal-active': modalActive}">
|
||||
<BaseButton
|
||||
v-if="menuActive"
|
||||
@click="$store.commit('menuActive', false)"
|
||||
class="menu-hide-button"
|
||||
class="menu-hide-button"
|
||||
>
|
||||
<icon icon="times" />
|
||||
<icon icon="times"/>
|
||||
</BaseButton>
|
||||
<div
|
||||
:class="{'has-background': background}"
|
||||
:style="{'background-image': background && `url(${background})`}"
|
||||
:class="{'has-background': background || blurHash}"
|
||||
:style="{'background-image': blurHash && `url(${blurHash})`}"
|
||||
class="app-container"
|
||||
>
|
||||
<div
|
||||
:class="{'is-visible': background}"
|
||||
class="app-container-background background-fade-in"
|
||||
:style="{'background-image': background && `url(${background})`}"></div>
|
||||
<navigation/>
|
||||
<main
|
||||
:class="[
|
||||
|
@ -31,18 +35,18 @@
|
|||
|
||||
<router-view :route="routeWithModal" v-slot="{ Component }">
|
||||
<keep-alive :include="['list.list', 'list.gantt', 'list.table', 'list.kanban']">
|
||||
<component :is="Component" />
|
||||
<component :is="Component"/>
|
||||
</keep-alive>
|
||||
</router-view>
|
||||
|
||||
<transition name="modal">
|
||||
<modal
|
||||
v-if="currentModal"
|
||||
v-if="currentModal"
|
||||
@close="closeModal()"
|
||||
variant="scrolling"
|
||||
class="task-detail-view-modal"
|
||||
>
|
||||
<component :is="currentModal" />
|
||||
<component :is="currentModal"/>
|
||||
</modal>
|
||||
</transition>
|
||||
|
||||
|
@ -115,16 +119,17 @@ function useRouteWithModal() {
|
|||
}
|
||||
}
|
||||
|
||||
return { routeWithModal, currentModal, closeModal }
|
||||
return {routeWithModal, currentModal, closeModal}
|
||||
}
|
||||
|
||||
const { routeWithModal, currentModal, closeModal } = useRouteWithModal()
|
||||
|
||||
const {routeWithModal, currentModal, closeModal} = useRouteWithModal()
|
||||
|
||||
const store = useStore()
|
||||
|
||||
const background = computed(() => store.state.background)
|
||||
const blurHash = computed(() => store.state.blurHash)
|
||||
const menuActive = computed(() => store.state.menuActive)
|
||||
const modalActive = computed(() => store.state.modalActive)
|
||||
|
||||
function showKeyboardShortcuts() {
|
||||
store.commit(KEYBOARD_SHORTCUTS_ACTIVE, true)
|
||||
|
@ -151,7 +156,7 @@ watch(() => route.name as string, (routeName) => {
|
|||
'migrate.start',
|
||||
'migrate.wunderlist',
|
||||
'namespaces.index',
|
||||
].includes(routeName) ||
|
||||
].includes(routeName) ||
|
||||
routeName.startsWith('user.settings')
|
||||
)
|
||||
) {
|
||||
|
@ -163,7 +168,7 @@ watch(() => route.name as string, (routeName) => {
|
|||
|
||||
function useRenewTokenOnFocus() {
|
||||
const router = useRouter()
|
||||
|
||||
|
||||
const userInfo = computed(() => store.state.auth.info)
|
||||
const authenticated = computed(() => store.state.auth.authenticated)
|
||||
|
||||
|
@ -227,40 +232,41 @@ store.dispatch('labels/loadAllLabels')
|
|||
}
|
||||
|
||||
.app-container {
|
||||
min-height: calc(100vh - 65px);
|
||||
min-height: calc(100vh - 65px);
|
||||
|
||||
@media screen and (max-width: $tablet) {
|
||||
padding-top: $navbar-height;
|
||||
}
|
||||
|
||||
.app-content {
|
||||
padding: $navbar-height + 1.5rem 1.5rem 1rem 1.5rem;
|
||||
z-index: 2;
|
||||
|
||||
@media screen and (max-width: $tablet) {
|
||||
margin-left: 0;
|
||||
padding-top: 1.5rem;
|
||||
min-height: calc(100vh - 4rem);
|
||||
}
|
||||
|
||||
&.is-menu-enabled {
|
||||
margin-left: $navbar-width;
|
||||
|
||||
@media screen and (max-width: $tablet) {
|
||||
min-width: 100%;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.task\.detail {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
@media screen and (max-width: $tablet) {
|
||||
padding-top: $navbar-height;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: var(--white);
|
||||
}
|
||||
}
|
||||
.app-content {
|
||||
padding: $navbar-height + 1.5rem 1.5rem 1rem 1.5rem;
|
||||
z-index: 10;
|
||||
position: relative;
|
||||
|
||||
// Used to make sure the spinner is always in the middle while loading
|
||||
> .loader-container {
|
||||
min-height: calc(100vh - #{$navbar-height + 1.5rem + 1rem});
|
||||
}
|
||||
|
||||
@media screen and (max-width: $tablet) {
|
||||
margin-left: 0;
|
||||
padding-top: 1.5rem;
|
||||
min-height: calc(100vh - 4rem);
|
||||
}
|
||||
|
||||
&.is-menu-enabled {
|
||||
margin-left: $navbar-width;
|
||||
|
||||
@media screen and (max-width: $tablet) {
|
||||
min-width: 100%;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.card {
|
||||
background: var(--white);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mobile-overlay {
|
||||
|
@ -270,7 +276,9 @@ store.dispatch('labels/loadAllLabels')
|
|||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: rgba(250, 250, 250, 0.8);
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
background: hsla(var(--grey-100-hsl), 0.8);
|
||||
z-index: 5;
|
||||
opacity: 0;
|
||||
transition: all $transition;
|
||||
|
@ -289,11 +297,20 @@ store.dispatch('labels/loadAllLabels')
|
|||
|
||||
color: var(--grey-500);
|
||||
transition: color $transition;
|
||||
|
||||
|
||||
@media screen and (max-width: $tablet) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.content-auth {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
|
||||
&.modal-active {
|
||||
z-index: unset;
|
||||
}
|
||||
}
|
||||
|
||||
@include modal-transition();
|
||||
</style>
|
|
@ -6,7 +6,7 @@
|
|||
</router-link>
|
||||
<ul class="menu-list">
|
||||
<li>
|
||||
<router-link :to="{ name: 'home'}">
|
||||
<router-link :to="{ name: 'home'}" v-shortcut="'g o'">
|
||||
<span class="icon">
|
||||
<icon icon="calendar"/>
|
||||
</span>
|
||||
|
@ -14,7 +14,7 @@
|
|||
</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<router-link :to="{ name: 'tasks.range'}">
|
||||
<router-link :to="{ name: 'tasks.range'}" v-shortcut="'g u'">
|
||||
<span class="icon">
|
||||
<icon :icon="['far', 'calendar-alt']"/>
|
||||
</span>
|
||||
|
@ -22,7 +22,7 @@
|
|||
</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<router-link :to="{ name: 'namespaces.index'}">
|
||||
<router-link :to="{ name: 'namespaces.index'}" v-shortcut="'g n'">
|
||||
<span class="icon">
|
||||
<icon icon="layer-group"/>
|
||||
</span>
|
||||
|
@ -30,7 +30,7 @@
|
|||
</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<router-link :to="{ name: 'labels.index'}">
|
||||
<router-link :to="{ name: 'labels.index'}" v-shortcut="'g a'">
|
||||
<span class="icon">
|
||||
<icon icon="tags"/>
|
||||
</span>
|
||||
|
@ -38,7 +38,7 @@
|
|||
</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<router-link :to="{ name: 'teams.index'}">
|
||||
<router-link :to="{ name: 'teams.index'}" v-shortcut="'g m'">
|
||||
<span class="icon">
|
||||
<icon icon="users"/>
|
||||
</span>
|
||||
|
|
|
@ -66,7 +66,7 @@ const showIconOnly = computed(() => props.icon !== '' && typeof slots.default ==
|
|||
text-transform: uppercase;
|
||||
font-size: 0.85rem;
|
||||
font-weight: bold;
|
||||
height: $button-height;
|
||||
min-height: $button-height;
|
||||
box-shadow: var(--shadow-sm);
|
||||
display: inline-flex;
|
||||
|
||||
|
|
|
@ -183,6 +183,10 @@ export default {
|
|||
this.updateData()
|
||||
},
|
||||
get() {
|
||||
if(!this.date) {
|
||||
return ''
|
||||
}
|
||||
|
||||
return format(this.date, 'yyy-LL-dd H:mm')
|
||||
},
|
||||
},
|
||||
|
|
|
@ -326,7 +326,7 @@ export default {
|
|||
}
|
||||
|
||||
&.has-checkbox {
|
||||
margin-left: -2em;
|
||||
margin-left: -1.25rem;
|
||||
list-style: none;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -450,7 +450,6 @@ export default {
|
|||
|
||||
button {
|
||||
background: transparent;
|
||||
display: block;
|
||||
text-align: left;
|
||||
box-shadow: none;
|
||||
border-radius: 0;
|
||||
|
@ -460,6 +459,7 @@ export default {
|
|||
padding: .5rem;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
color: var(--grey-800);
|
||||
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
<a
|
||||
@click="togglePasswordFieldType"
|
||||
class="password-field-type-toggle"
|
||||
aria-label="passwordFieldType === 'password' ? $t('user.auth.showPassword') : $t('user.auth.hidePassword')"
|
||||
:aria-label="passwordFieldType === 'password' ? $t('user.auth.showPassword') : $t('user.auth.hidePassword')"
|
||||
v-tooltip="passwordFieldType === 'password' ? $t('user.auth.showPassword') : $t('user.auth.hidePassword')">
|
||||
<icon :icon="passwordFieldType === 'password' ? 'eye' : 'eye-slash'"/>
|
||||
</a>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
>
|
||||
{{ $t('filters.clear') }}
|
||||
</x-button>
|
||||
<popup>
|
||||
<popup :has-overflow="true">
|
||||
<template #trigger="{toggle}">
|
||||
<x-button
|
||||
@click.prevent.stop="toggle()"
|
||||
|
|
|
@ -67,49 +67,49 @@
|
|||
<div class="field">
|
||||
<label class="label">{{ $t('task.attributes.dueDate') }}</label>
|
||||
<div class="control">
|
||||
<flat-pickr
|
||||
:config="flatPickerConfig"
|
||||
@on-close="setDueDateFilter"
|
||||
class="input"
|
||||
:placeholder="$t('filters.attributes.dueDateRange')"
|
||||
v-model="filters.dueDate"
|
||||
/>
|
||||
<datepicker-with-range @dateChanged="values => setDateFilter('due_date', values)">
|
||||
<template #trigger="{toggle, buttonText}">
|
||||
<x-button @click.prevent.stop="toggle()" variant="secondary" :shadow="false" class="mb-2">
|
||||
{{ buttonText }}
|
||||
</x-button>
|
||||
</template>
|
||||
</datepicker-with-range>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">{{ $t('task.attributes.startDate') }}</label>
|
||||
<div class="control">
|
||||
<flat-pickr
|
||||
:config="flatPickerConfig"
|
||||
@on-close="setStartDateFilter"
|
||||
class="input"
|
||||
:placeholder="$t('filters.attributes.startDateRange')"
|
||||
v-model="filters.startDate"
|
||||
/>
|
||||
<datepicker-with-range @dateChanged="values => setDateFilter('start_date', values)">
|
||||
<template #trigger="{toggle, buttonText}">
|
||||
<x-button @click.prevent.stop="toggle()" variant="secondary" :shadow="false" class="mb-2">
|
||||
{{ buttonText }}
|
||||
</x-button>
|
||||
</template>
|
||||
</datepicker-with-range>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">{{ $t('task.attributes.endDate') }}</label>
|
||||
<div class="control">
|
||||
<flat-pickr
|
||||
:config="flatPickerConfig"
|
||||
@on-close="setEndDateFilter"
|
||||
class="input"
|
||||
:placeholder="$t('filters.attributes.endDateRange')"
|
||||
v-model="filters.endDate"
|
||||
/>
|
||||
<datepicker-with-range @dateChanged="values => setDateFilter('end_date', values)">
|
||||
<template #trigger="{toggle, buttonText}">
|
||||
<x-button @click.prevent.stop="toggle()" variant="secondary" :shadow="false" class="mb-2">
|
||||
{{ buttonText }}
|
||||
</x-button>
|
||||
</template>
|
||||
</datepicker-with-range>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">{{ $t('task.attributes.reminders') }}</label>
|
||||
<div class="control">
|
||||
<flat-pickr
|
||||
:config="flatPickerConfig"
|
||||
@on-close="setReminderFilter"
|
||||
class="input"
|
||||
:placeholder="$t('filters.attributes.reminderRange')"
|
||||
v-model="filters.reminders"
|
||||
/>
|
||||
<datepicker-with-range @dateChanged="values => setDateFilter('reminders', values)">
|
||||
<template #trigger="{toggle, buttonText}">
|
||||
<x-button @click.prevent.stop="toggle()" variant="secondary" :shadow="false" class="mb-2">
|
||||
{{ buttonText }}
|
||||
</x-button>
|
||||
</template>
|
||||
</datepicker-with-range>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -175,15 +175,14 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import DatepickerWithRange from '@/components/date/datepickerWithRange'
|
||||
import Fancycheckbox from '../../input/fancycheckbox'
|
||||
import flatPickr from 'vue-flatpickr-component'
|
||||
import 'flatpickr/dist/flatpickr.css'
|
||||
|
||||
import {includesById} from '@/helpers/utils'
|
||||
import {formatISO} from 'date-fns'
|
||||
import PrioritySelect from '@/components/tasks/partials/prioritySelect.vue'
|
||||
import PercentDoneSelect from '@/components/tasks/partials/percentDoneSelect.vue'
|
||||
import Multiselect from '@/components/input/multiselect.vue'
|
||||
import {parseDateOrString} from '@/helpers/time/parseDateOrString'
|
||||
|
||||
import UserService from '@/services/user'
|
||||
import ListService from '@/services/list'
|
||||
|
@ -222,15 +221,15 @@ const DEFAULT_FILTERS = {
|
|||
namespace: '',
|
||||
}
|
||||
|
||||
export const ALPHABETICAL_SORT = 'title'
|
||||
export const ALPHABETICAL_SORT = 'title'
|
||||
|
||||
export default {
|
||||
name: 'filters',
|
||||
components: {
|
||||
DatepickerWithRange,
|
||||
EditLabels,
|
||||
PrioritySelect,
|
||||
Fancycheckbox,
|
||||
flatPickr,
|
||||
PercentDoneSelect,
|
||||
Multiselect,
|
||||
},
|
||||
|
@ -281,7 +280,7 @@ export default {
|
|||
return this.params?.sort_by?.find(sortBy => sortBy === ALPHABETICAL_SORT) !== undefined
|
||||
},
|
||||
set(sortAlphabetically) {
|
||||
this.params.sort_by = sortAlphabetically
|
||||
this.params.sort_by = sortAlphabetically
|
||||
? [ALPHABETICAL_SORT]
|
||||
: getDefaultParams().sort_by
|
||||
|
||||
|
@ -291,19 +290,6 @@ export default {
|
|||
foundLabels() {
|
||||
return this.$store.getters['labels/filterLabelsByQuery'](this.labels, this.query)
|
||||
},
|
||||
flatPickerConfig() {
|
||||
return {
|
||||
altFormat: this.$t('date.altFormatLong'),
|
||||
altInput: true,
|
||||
dateFormat: 'Y-m-d H:i',
|
||||
enableTime: true,
|
||||
time_24hr: true,
|
||||
mode: 'range',
|
||||
locale: {
|
||||
firstDayOfWeek: this.$store.state.auth.settings.weekStart,
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
change() {
|
||||
|
@ -343,19 +329,12 @@ export default {
|
|||
}
|
||||
}
|
||||
},
|
||||
setDateFilter(filterName, variableName = null) {
|
||||
if (variableName === null) {
|
||||
variableName = filterName
|
||||
}
|
||||
|
||||
// Only filter if we have a start and end due date
|
||||
if (this.filters[variableName] !== '') {
|
||||
setDateFilter(filterName, {dateFrom, dateTo}) {
|
||||
dateFrom = parseDateOrString(dateFrom, null)
|
||||
dateTo = parseDateOrString(dateTo, null)
|
||||
|
||||
const parts = this.filters[variableName].split(' to ')
|
||||
|
||||
if (parts.length < 2) {
|
||||
return
|
||||
}
|
||||
// Only filter if we have a date
|
||||
if (dateFrom !== null && dateTo !== null) {
|
||||
|
||||
// Check if we already have values in params and only update them if we do
|
||||
let foundStart = false
|
||||
|
@ -363,23 +342,23 @@ export default {
|
|||
this.params.filter_by.forEach((f, i) => {
|
||||
if (f === filterName && this.params.filter_comparator[i] === 'greater_equals') {
|
||||
foundStart = true
|
||||
this.params.filter_value[i] = formatISO(new Date(parts[0]))
|
||||
this.params.filter_value[i] = dateFrom
|
||||
}
|
||||
if (f === filterName && this.params.filter_comparator[i] === 'less_equals') {
|
||||
foundEnd = true
|
||||
this.params.filter_value[i] = formatISO(new Date(parts[1]))
|
||||
this.params.filter_value[i] = dateTo
|
||||
}
|
||||
})
|
||||
|
||||
if (!foundStart) {
|
||||
this.params.filter_by.push(filterName)
|
||||
this.params.filter_comparator.push('greater_equals')
|
||||
this.params.filter_value.push(formatISO(new Date(parts[0])))
|
||||
this.params.filter_value.push(dateFrom)
|
||||
}
|
||||
if (!foundEnd) {
|
||||
this.params.filter_by.push(filterName)
|
||||
this.params.filter_comparator.push('less_equals')
|
||||
this.params.filter_value.push(formatISO(new Date(parts[1])))
|
||||
this.params.filter_value.push(dateTo)
|
||||
}
|
||||
this.change()
|
||||
return
|
||||
|
@ -513,24 +492,12 @@ export default {
|
|||
this.params.filter_concat = 'or'
|
||||
}
|
||||
},
|
||||
setDueDateFilter() {
|
||||
this.setDateFilter('due_date', 'dueDate')
|
||||
},
|
||||
setPriority() {
|
||||
this.setSingleValueFilter('priority', 'priority', 'usePriority')
|
||||
},
|
||||
setStartDateFilter() {
|
||||
this.setDateFilter('start_date', 'startDate')
|
||||
},
|
||||
setEndDateFilter() {
|
||||
this.setDateFilter('end_date', 'endDate')
|
||||
},
|
||||
setPercentDoneFilter() {
|
||||
this.setSingleValueFilter('percent_done', 'percentDone', 'usePercentDone')
|
||||
},
|
||||
setReminderFilter() {
|
||||
this.setDateFilter('reminders')
|
||||
},
|
||||
clear(kind) {
|
||||
this[`found${kind}`] = []
|
||||
},
|
||||
|
@ -609,7 +576,7 @@ export default {
|
|||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
<style lang="scss" scoped>
|
||||
.single-value-control {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
@ -618,4 +585,8 @@ export default {
|
|||
margin-left: .5rem;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.datepicker-with-range-container .popup) {
|
||||
right: 0;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -2,28 +2,34 @@
|
|||
<router-link
|
||||
:class="{
|
||||
'has-light-text': !colorIsDark(list.hexColor),
|
||||
'has-background': background !== null
|
||||
'has-background': blurHashUrl !== ''
|
||||
}"
|
||||
:style="{
|
||||
'background-color': list.hexColor,
|
||||
'background-image': background !== null ? `url(${background})` : false,
|
||||
'background-image': blurHashUrl !== null ? `url(${blurHashUrl})` : false,
|
||||
}"
|
||||
:to="{ name: 'list.index', params: { listId: list.id} }"
|
||||
class="list-card"
|
||||
v-if="list !== null && (showArchived ? true : !list.isArchived)"
|
||||
>
|
||||
<div class="is-archived-container">
|
||||
<div
|
||||
class="list-background background-fade-in"
|
||||
:class="{'is-visible': background}"
|
||||
:style="{'background-image': background !== null ? `url(${background})` : false}"></div>
|
||||
<div class="list-content">
|
||||
<div class="is-archived-container">
|
||||
<span class="is-archived" v-if="list.isArchived">
|
||||
{{ $t('namespace.archived') }}
|
||||
</span>
|
||||
<span
|
||||
:class="{'is-favorite': list.isFavorite, 'is-archived': list.isArchived}"
|
||||
@click.stop="toggleFavoriteList(list)"
|
||||
class="favorite">
|
||||
<icon :icon="list.isFavorite ? 'star' : ['far', 'star']" />
|
||||
<span
|
||||
:class="{'is-favorite': list.isFavorite, 'is-archived': list.isArchived}"
|
||||
@click.stop="toggleFavoriteList(list)"
|
||||
class="favorite">
|
||||
<icon :icon="list.isFavorite ? 'star' : ['far', 'star']"/>
|
||||
</span>
|
||||
</div>
|
||||
<div class="title">{{ list.title }}</div>
|
||||
</div>
|
||||
<div class="title">{{ list.title }}</div>
|
||||
</router-link>
|
||||
</template>
|
||||
|
||||
|
@ -32,12 +38,14 @@ import {PropType, ref, watch} from 'vue'
|
|||
import {useStore} from 'vuex'
|
||||
|
||||
import ListService from '@/services/list'
|
||||
import {getBlobFromBlurHash} from '@/helpers/getBlobFromBlurHash'
|
||||
|
||||
import {colorIsDark} from '@/helpers/color/colorIsDark'
|
||||
import ListModel from '@/models/list'
|
||||
|
||||
const background = ref<string | null>(null)
|
||||
const backgroundLoading = ref(false)
|
||||
const blurHashUrl = ref('')
|
||||
|
||||
const props = defineProps({
|
||||
list: {
|
||||
|
@ -50,13 +58,18 @@ const props = defineProps({
|
|||
},
|
||||
})
|
||||
|
||||
watch(props.list, loadBackground, { immediate: true })
|
||||
watch(props.list, loadBackground, {immediate: true})
|
||||
|
||||
async function loadBackground() {
|
||||
if (props.list === null || !props.list.backgroundInformation || backgroundLoading.value) {
|
||||
return
|
||||
}
|
||||
|
||||
const blurHash = await getBlobFromBlurHash(props.list.backgroundBlurHash)
|
||||
if (blurHash) {
|
||||
blurHashUrl.value = window.URL.createObjectURL(blurHash)
|
||||
}
|
||||
|
||||
backgroundLoading.value = true
|
||||
|
||||
const listService = new ListService()
|
||||
|
@ -81,129 +94,145 @@ function toggleFavoriteList(list: ListModel) {
|
|||
|
||||
<style lang="scss" scoped>
|
||||
.list-card {
|
||||
cursor: pointer;
|
||||
width: calc((100% - #{($lists-per-row - 1) * 1rem}) / #{$lists-per-row});
|
||||
height: $list-height;
|
||||
background: var(--white);
|
||||
margin: 0 $list-spacing $list-spacing 0;
|
||||
padding: 1rem;
|
||||
border-radius: $radius;
|
||||
box-shadow: var(--shadow-sm);
|
||||
transition: box-shadow $transition;
|
||||
cursor: pointer;
|
||||
width: calc((100% - #{($lists-per-row - 1) * 1rem}) / #{$lists-per-row});
|
||||
height: $list-height;
|
||||
background: var(--white);
|
||||
margin: 0 $list-spacing $list-spacing 0;
|
||||
border-radius: $radius;
|
||||
box-shadow: var(--shadow-sm);
|
||||
transition: box-shadow $transition;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
&.has-light-text .title {
|
||||
color: var(--light);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
&.has-background, .list-background {
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
&:active,
|
||||
&:focus,
|
||||
&:focus:not(:active) {
|
||||
box-shadow: var(--shadow-xs) !important;
|
||||
}
|
||||
&.has-background .list-content .title {
|
||||
text-shadow: 0 0 10px var(--black), 1px 1px 5px var(--grey-700), -1px -1px 5px var(--grey-700);
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
@media screen and (min-width: $widescreen) {
|
||||
&:nth-child(#{$lists-per-row}n) {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
.list-background {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
@media screen and (max-width: $widescreen) and (min-width: $tablet) {
|
||||
$lists-per-row: 3;
|
||||
& {
|
||||
width: calc((100% - #{($lists-per-row - 1) * 1rem}) / #{$lists-per-row});
|
||||
}
|
||||
&:hover {
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
&:nth-child(#{$lists-per-row}n) {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
&:active,
|
||||
&:focus,
|
||||
&:focus:not(:active) {
|
||||
box-shadow: var(--shadow-xs) !important;
|
||||
}
|
||||
|
||||
@media screen and (max-width: $tablet) {
|
||||
$lists-per-row: 2;
|
||||
& {
|
||||
width: calc((100% - #{($lists-per-row - 1) * 1rem}) / #{$lists-per-row});
|
||||
}
|
||||
@media screen and (min-width: $widescreen) {
|
||||
&:nth-child(#{$lists-per-row}n) {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-child(#{$lists-per-row}n) {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: $widescreen) and (min-width: $tablet) {
|
||||
$lists-per-row: 3;
|
||||
& {
|
||||
width: calc((100% - #{($lists-per-row - 1) * 1rem}) / #{$lists-per-row});
|
||||
}
|
||||
|
||||
@media screen and (max-width: $mobile) {
|
||||
$lists-per-row: 1;
|
||||
& {
|
||||
width: 100%;
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
&:nth-child(#{$lists-per-row}n) {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.is-archived-container {
|
||||
width: 100%;
|
||||
text-align: right;
|
||||
@media screen and (max-width: $tablet) {
|
||||
$lists-per-row: 2;
|
||||
& {
|
||||
width: calc((100% - #{($lists-per-row - 1) * 1rem}) / #{$lists-per-row});
|
||||
}
|
||||
|
||||
.is-archived {
|
||||
font-size: .75rem;
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
&:nth-child(#{$lists-per-row}n) {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
align-self: flex-end;
|
||||
font-family: $vikunja-font;
|
||||
font-weight: 400;
|
||||
font-size: 1.5rem;
|
||||
color: var(--text);
|
||||
width: 100%;
|
||||
margin-bottom: 0;
|
||||
max-height: calc(100% - 2rem); // 1rem padding, 1rem height of the "is archived" badge
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
@media screen and (max-width: $mobile) {
|
||||
$lists-per-row: 1;
|
||||
& {
|
||||
width: 100%;
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
.list-content {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
padding: 1rem;
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
&.has-light-text .title {
|
||||
color: var(--light);
|
||||
}
|
||||
.is-archived-container {
|
||||
width: 100%;
|
||||
text-align: right;
|
||||
|
||||
&.has-background {
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
.is-archived {
|
||||
font-size: .75rem;
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
text-shadow: 0 0 10px var(--black), 1px 1px 5px var(--grey-700), -1px -1px 5px var(--grey-700);
|
||||
color: var(--white);
|
||||
}
|
||||
}
|
||||
.title {
|
||||
align-self: flex-end;
|
||||
font-family: $vikunja-font;
|
||||
font-weight: 400;
|
||||
font-size: 1.5rem;
|
||||
color: var(--text);
|
||||
width: 100%;
|
||||
margin-bottom: 0;
|
||||
max-height: calc(100% - 2rem); // 1rem padding, 1rem height of the "is archived" badge
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
.favorite {
|
||||
transition: opacity $transition, color $transition;
|
||||
opacity: 0;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--warning);
|
||||
}
|
||||
.favorite {
|
||||
transition: opacity $transition, color $transition;
|
||||
opacity: 0;
|
||||
|
||||
&.is-archived {
|
||||
display: none;
|
||||
}
|
||||
&:hover {
|
||||
color: var(--warning);
|
||||
}
|
||||
|
||||
&.is-favorite {
|
||||
display: inline-block;
|
||||
opacity: 1;
|
||||
color: var(--warning);
|
||||
}
|
||||
}
|
||||
&.is-archived {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover .favorite {
|
||||
opacity: 1;
|
||||
}
|
||||
&.is-favorite {
|
||||
display: inline-block;
|
||||
opacity: 1;
|
||||
color: var(--warning);
|
||||
}
|
||||
}
|
||||
|
||||
&:hover .favorite {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -39,79 +39,68 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Message from '@/components/misc/message'
|
||||
<script setup lang="ts">
|
||||
import {ref, computed, watch} from 'vue'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
import {parseURL} from 'ufo'
|
||||
|
||||
import {checkAndSetApiUrl} from '@/helpers/checkAndSetApiUrl'
|
||||
import {success} from '@/message'
|
||||
|
||||
export default {
|
||||
name: 'apiConfig',
|
||||
components: {
|
||||
Message,
|
||||
import Message from '@/components/misc/message.vue'
|
||||
|
||||
const props = defineProps({
|
||||
configureOpen: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
configureApi: false,
|
||||
apiUrl: window.API_URL,
|
||||
errorMsg: '',
|
||||
successMsg: '',
|
||||
})
|
||||
const emit = defineEmits(['foundApi'])
|
||||
|
||||
const apiUrl = ref(window.API_URL)
|
||||
const configureApi = ref(apiUrl.value === '')
|
||||
|
||||
// Because we're only using this to parse the hostname, it should be fine to just prefix with http://
|
||||
// regardless of whether the url is actually reachable under http.
|
||||
const apiDomain = computed(() => parseURL(apiUrl.value, 'http://').host || parseURL(window.location.href).host)
|
||||
|
||||
watch(() => props.configureOpen, (value) => {
|
||||
configureApi.value = value
|
||||
}, {immediate: true})
|
||||
|
||||
|
||||
const {t} = useI18n()
|
||||
|
||||
const errorMsg = ref('')
|
||||
const successMsg = ref('')
|
||||
|
||||
async function setApiUrl() {
|
||||
if (apiUrl.value === '') {
|
||||
// Don't try to check and set an empty url
|
||||
errorMsg.value = t('apiConfig.urlRequired')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const url = await checkAndSetApiUrl(apiUrl.value)
|
||||
|
||||
if (url === '') {
|
||||
// If the config setter function could not figure out a url
|
||||
throw new Error('URL cannot be empty.')
|
||||
}
|
||||
},
|
||||
emits: ['foundApi'],
|
||||
created() {
|
||||
if (this.apiUrl === '') {
|
||||
this.configureApi = true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
apiDomain() {
|
||||
return parseURL(this.apiUrl).host || parseURL(window.location.href).host
|
||||
},
|
||||
},
|
||||
props: {
|
||||
configureOpen: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
configureOpen: {
|
||||
handler(value) {
|
||||
this.configureApi = value
|
||||
},
|
||||
immediate: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async setApiUrl() {
|
||||
if (this.apiUrl === '') {
|
||||
// Don't try to check and set an empty url
|
||||
this.errorMsg = this.$t('apiConfig.urlRequired')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const url = await checkAndSetApiUrl(this.apiUrl)
|
||||
|
||||
if (url === '') {
|
||||
// If the config setter function could not figure out a url
|
||||
throw new Error('URL cannot be empty.')
|
||||
}
|
||||
|
||||
// Set it + save it to local storage to save us the hoops
|
||||
this.errorMsg = ''
|
||||
this.$message.success({message: this.$t('apiConfig.success', {domain: this.apiDomain})})
|
||||
this.configureApi = false
|
||||
this.apiUrl = url
|
||||
this.$emit('foundApi', this.apiUrl)
|
||||
} catch (e) {
|
||||
// Still not found, url is still invalid
|
||||
this.successMsg = ''
|
||||
this.errorMsg = this.$t('apiConfig.error', {domain: this.apiDomain})
|
||||
}
|
||||
},
|
||||
},
|
||||
// Set it + save it to local storage to save us the hoops
|
||||
errorMsg.value = ''
|
||||
apiUrl.value = url
|
||||
success({message: t('apiConfig.success', {domain: apiDomain.value})})
|
||||
configureApi.value = false
|
||||
emit('foundApi', apiUrl.value)
|
||||
} catch (e) {
|
||||
// Still not found, url is still invalid
|
||||
successMsg.value = ''
|
||||
errorMsg.value = t('apiConfig.error', {domain: apiDomain.value})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -6,9 +6,13 @@
|
|||
|
||||
<message class="mb-4" v-if="s.available">
|
||||
{{
|
||||
s.available($route)
|
||||
? $t('keyboardShortcuts.currentPageOnly')
|
||||
: $t('keyboardShortcuts.allPages')
|
||||
typeof s.available === 'undefined' ?
|
||||
$t('keyboardShortcuts.allPages') :
|
||||
(
|
||||
s.available($route)
|
||||
? $t('keyboardShortcuts.currentPageOnly')
|
||||
: $t('keyboardShortcuts.somePagesOnly')
|
||||
)
|
||||
}}
|
||||
</message>
|
||||
|
||||
|
@ -38,6 +42,7 @@ import {KEYBOARD_SHORTCUTS_ACTIVE} from '@/store/mutation-types'
|
|||
import {KEYBOARD_SHORTCUTS as shortcuts} from './shortcuts'
|
||||
|
||||
const store = useStore()
|
||||
|
||||
function close() {
|
||||
store.commit(KEYBOARD_SHORTCUTS_ACTIVE, false)
|
||||
}
|
||||
|
|
|
@ -30,6 +30,36 @@ export const KEYBOARD_SHORTCUTS : ShortcutGroup[] = [
|
|||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'keyboardShortcuts.navigation.title',
|
||||
shortcuts: [
|
||||
{
|
||||
title: 'keyboardShortcuts.navigation.overview',
|
||||
keys: ['g', 'o'],
|
||||
combination: 'then',
|
||||
},
|
||||
{
|
||||
title: 'keyboardShortcuts.navigation.upcoming',
|
||||
keys: ['g', 'u'],
|
||||
combination: 'then',
|
||||
},
|
||||
{
|
||||
title: 'keyboardShortcuts.navigation.namespaces',
|
||||
keys: ['g', 'n'],
|
||||
combination: 'then',
|
||||
},
|
||||
{
|
||||
title: 'keyboardShortcuts.navigation.labels',
|
||||
keys: ['g', 'a'],
|
||||
combination: 'then',
|
||||
},
|
||||
{
|
||||
title: 'keyboardShortcuts.navigation.teams',
|
||||
keys: ['g', 'm'],
|
||||
combination: 'then',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'list.kanban.title',
|
||||
available: (route) => route.name === 'list.kanban',
|
||||
|
@ -70,6 +100,10 @@ export const KEYBOARD_SHORTCUTS : ShortcutGroup[] = [
|
|||
title: 'keyboardShortcuts.task.title',
|
||||
available: (route) => route.name === 'task.detail',
|
||||
shortcuts: [
|
||||
{
|
||||
title: 'keyboardShortcuts.task.done',
|
||||
keys: ['t'],
|
||||
},
|
||||
{
|
||||
title: 'keyboardShortcuts.task.assign',
|
||||
keys: ['a'],
|
||||
|
@ -90,6 +124,14 @@ export const KEYBOARD_SHORTCUTS : ShortcutGroup[] = [
|
|||
title: 'keyboardShortcuts.task.related',
|
||||
keys: ['r'],
|
||||
},
|
||||
{
|
||||
title: 'keyboardShortcuts.task.move',
|
||||
keys: ['m'],
|
||||
},
|
||||
{
|
||||
title: 'keyboardShortcuts.task.color',
|
||||
keys: ['c'],
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<slot name="trigger" :isOpen="open" :toggle="toggle"></slot>
|
||||
<div class="popup" :class="{'is-open': open}" ref="popup">
|
||||
<div class="popup" :class="{'is-open': open, 'has-overflow': props.hasOverflow && open}" ref="popup">
|
||||
<slot name="content" :isOpen="open"/>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -16,6 +16,13 @@ const toggle = () => {
|
|||
open.value = !open.value
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
hasOverflow: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
})
|
||||
|
||||
function hidePopup(e) {
|
||||
if (!open.value) {
|
||||
return
|
||||
|
|
|
@ -8,10 +8,10 @@
|
|||
{ 'has-overflow': overflow },
|
||||
variant,
|
||||
]"
|
||||
ref="modal"
|
||||
>
|
||||
<div
|
||||
class="modal-container"
|
||||
:class="{'has-overflow': overflow}"
|
||||
@click.self.prevent.stop="$emit('close')"
|
||||
v-shortcut="'Escape'"
|
||||
>
|
||||
|
@ -60,65 +60,41 @@
|
|||
</transition>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script lang="ts" setup>
|
||||
import BaseButton from '@/components/base/BaseButton.vue'
|
||||
import {ref, watch} from 'vue'
|
||||
import {useScrollLock} from '@vueuse/core'
|
||||
import {useStore} from 'vuex'
|
||||
|
||||
export const TRANSITION_NAMES = {
|
||||
MODAL: 'modal',
|
||||
FADE: 'fade',
|
||||
}
|
||||
const store = useStore()
|
||||
|
||||
export const VARIANTS = {
|
||||
DEFAULT: 'default',
|
||||
HINT_MODAL: 'hint-modal',
|
||||
SCROLLING: 'scrolling',
|
||||
}
|
||||
const props = withDefaults(defineProps<{
|
||||
enabled?: boolean,
|
||||
overflow?: boolean,
|
||||
wide?: boolean,
|
||||
transitionName?: 'modal' | 'fade',
|
||||
variant?: 'default' | 'hint-modal' | 'scrolling',
|
||||
}>(), {
|
||||
enabled: true,
|
||||
transitionName: 'modal',
|
||||
variant: 'default',
|
||||
})
|
||||
|
||||
function validValue(values) {
|
||||
return (value) => Object.values(values).includes(value)
|
||||
}
|
||||
defineEmits(['close', 'submit'])
|
||||
|
||||
export default {
|
||||
name: 'modal',
|
||||
const modal = ref<HTMLElement | null>(null)
|
||||
const scrollLock = useScrollLock(modal)
|
||||
|
||||
components: {
|
||||
BaseButton,
|
||||
watch(
|
||||
() => props.enabled,
|
||||
enabled => {
|
||||
scrollLock.value = enabled
|
||||
store.commit('modalActive', enabled)
|
||||
},
|
||||
|
||||
mounted() {
|
||||
document.addEventListener('keydown', (e) => {
|
||||
// Close the model when escape is pressed
|
||||
if (e.keyCode === 27) {
|
||||
this.$emit('close')
|
||||
}
|
||||
})
|
||||
{
|
||||
immediate: true,
|
||||
},
|
||||
props: {
|
||||
enabled: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
overflow: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
wide: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
transitionName: {
|
||||
type: String,
|
||||
default: TRANSITION_NAMES.MODAL,
|
||||
validator: validValue(TRANSITION_NAMES),
|
||||
},
|
||||
variant: {
|
||||
type: String,
|
||||
default: VARIANTS.DEFAULT,
|
||||
validator: validValue(VARIANTS),
|
||||
},
|
||||
},
|
||||
emits: ['close', 'submit'],
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
@ -207,7 +183,6 @@ export default {
|
|||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -220,14 +195,14 @@ export default {
|
|||
font-size: 2rem;
|
||||
|
||||
@media screen and (max-width: $desktop) {
|
||||
color: var(--dark);
|
||||
color: var(--grey-900);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
// Close icon SVG uses currentColor, change the color to keep it visible
|
||||
.dark .task-detail-view-modal .close {
|
||||
.dark .close {
|
||||
color: var(--grey-900);
|
||||
}
|
||||
</style>
|
|
@ -81,7 +81,7 @@ export default {
|
|||
return this.notifications.filter(n => n.readAt === null).length
|
||||
},
|
||||
notifications() {
|
||||
return this.allNotifications.filter(n => n.name !== '')
|
||||
return this.allNotifications ? this.allNotifications.filter(n => n.name !== '') : []
|
||||
},
|
||||
...mapState({
|
||||
userInfo: state => state.auth.info,
|
||||
|
|
|
@ -5,12 +5,13 @@
|
|||
<textarea
|
||||
:disabled="taskService.loading || undefined"
|
||||
class="add-task-textarea input"
|
||||
:class="{'textarea-empty': newTaskTitle === ''}"
|
||||
:placeholder="$t('list.list.addPlaceholder')"
|
||||
rows="1"
|
||||
v-focus
|
||||
v-model="newTaskTitle"
|
||||
ref="newTaskInput"
|
||||
@keyup="errorMessage = ''"
|
||||
@keyup="resetEmptyTitleError"
|
||||
@keydown.enter="handleEnter"
|
||||
/>
|
||||
<span class="icon is-small is-left">
|
||||
|
@ -24,15 +25,18 @@
|
|||
@click="addTask()"
|
||||
icon="plus"
|
||||
:loading="taskService.loading"
|
||||
:aria-label="$t('list.list.add')"
|
||||
>
|
||||
{{ $t('list.list.add') }}
|
||||
<span class="button-text">
|
||||
{{ $t('list.list.add') }}
|
||||
</span>
|
||||
</x-button>
|
||||
</p>
|
||||
</div>
|
||||
<p class="help is-danger" v-if="errorMessage !== ''">
|
||||
{{ errorMessage }}
|
||||
</p>
|
||||
<quick-add-magic v-else />
|
||||
<quick-add-magic v-else/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -40,7 +44,7 @@
|
|||
import {ref, watch, unref, shallowReactive} from 'vue'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
import {useStore} from 'vuex'
|
||||
import { tryOnMounted, debouncedWatch, useWindowSize, MaybeRef } from '@vueuse/core'
|
||||
import {tryOnMounted, debouncedWatch, useWindowSize, MaybeRef} from '@vueuse/core'
|
||||
|
||||
import TaskService from '@/services/task'
|
||||
import QuickAddMagic from '@/components/tasks/partials/quick-add-magic.vue'
|
||||
|
@ -54,12 +58,12 @@ function useAutoHeightTextarea(value: MaybeRef<string>) {
|
|||
const minHeight = ref(0)
|
||||
|
||||
// adapted from https://github.com/LeaVerou/stretchy/blob/47f5f065c733029acccb755cae793009645809e2/src/stretchy.js#L34
|
||||
function resize(textareaEl: HTMLInputElement|undefined) {
|
||||
function resize(textareaEl: HTMLInputElement | undefined) {
|
||||
if (!textareaEl) return
|
||||
|
||||
let empty
|
||||
|
||||
// the value here is the the attribute value
|
||||
// the value here is the attribute value
|
||||
if (!textareaEl.value && textareaEl.placeholder) {
|
||||
empty = true
|
||||
textareaEl.value = textareaEl.placeholder
|
||||
|
@ -95,12 +99,12 @@ function useAutoHeightTextarea(value: MaybeRef<string>) {
|
|||
}
|
||||
})
|
||||
|
||||
const { width: windowWidth } = useWindowSize()
|
||||
const {width: windowWidth} = useWindowSize()
|
||||
|
||||
debouncedWatch(
|
||||
windowWidth,
|
||||
() => resize(textarea.value),
|
||||
{ debounce: 200 },
|
||||
{debounce: 200},
|
||||
)
|
||||
|
||||
// It is not possible to get notified of a change of the value attribute of a textarea without workarounds (setTimeout)
|
||||
|
@ -129,12 +133,18 @@ const emit = defineEmits(['taskAdded'])
|
|||
const newTaskTitle = ref('')
|
||||
const newTaskInput = useAutoHeightTextarea(newTaskTitle)
|
||||
|
||||
const { t } = useI18n()
|
||||
const {t} = useI18n()
|
||||
const store = useStore()
|
||||
|
||||
const taskService = shallowReactive(new TaskService())
|
||||
const errorMessage = ref('')
|
||||
|
||||
function resetEmptyTitleError() {
|
||||
if (newTaskTitle.value !== '') {
|
||||
errorMessage.value = ''
|
||||
}
|
||||
}
|
||||
|
||||
async function addTask() {
|
||||
if (newTaskTitle.value === '') {
|
||||
errorMessage.value = t('list.create.addTitleRequired')
|
||||
|
@ -194,9 +204,26 @@ function handleEnter(e: KeyboardEvent) {
|
|||
|
||||
.add-task-button {
|
||||
height: 2.5rem;
|
||||
|
||||
@media screen and (max-width: $mobile) {
|
||||
.button-text {
|
||||
display: none;
|
||||
}
|
||||
|
||||
:deep(.icon) {
|
||||
margin: 0 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.add-task-textarea {
|
||||
transition: border-color $transition;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
// Adding this class when the textarea has no text prevents the textarea from wrapping the placeholder.
|
||||
.textarea-empty {
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -114,7 +114,7 @@ export default {
|
|||
},
|
||||
|
||||
async removeLabel(label) {
|
||||
if (!this.taskId === 0) {
|
||||
if (this.taskId !== 0) {
|
||||
await this.$store.dispatch('tasks/removeLabel', {label: label, taskId: this.taskId})
|
||||
}
|
||||
|
||||
|
|
|
@ -104,11 +104,12 @@ export default {
|
|||
async toggleTaskDone(task) {
|
||||
this.loadingInternal = true
|
||||
try {
|
||||
const done = !task.done
|
||||
await this.$store.dispatch('tasks/update', {
|
||||
...task,
|
||||
done: !task.done,
|
||||
done,
|
||||
})
|
||||
if (task.done) {
|
||||
if (done) {
|
||||
playPop()
|
||||
}
|
||||
} finally {
|
||||
|
|
|
@ -83,7 +83,9 @@
|
|||
<span class="title">{{ rts.title }}</span>
|
||||
<div class="tasks">
|
||||
<div :key="t.id" class="task" v-for="t in rts.tasks">
|
||||
<router-link :to="{ name: $route.name, params: { id: t.id } }" :class="{ 'done': t.done}">
|
||||
<router-link
|
||||
:to="{ name: $route.name, params: { id: t.id } }"
|
||||
:class="{ 'is-strikethrough': t.done}">
|
||||
<span
|
||||
class="different-list"
|
||||
v-if="t.listId !== listId"
|
||||
|
@ -156,6 +158,7 @@ export default {
|
|||
relationToDelete: {},
|
||||
saved: false,
|
||||
showNewRelationForm: false,
|
||||
query: '',
|
||||
}
|
||||
},
|
||||
components: {
|
||||
|
@ -211,10 +214,20 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
async findTasks(query) {
|
||||
this.query = query
|
||||
this.foundTasks = await this.taskService.getAll({}, {s: query})
|
||||
},
|
||||
|
||||
async addTaskRelation() {
|
||||
if (this.newTaskRelationTask.id === 0 && this.query !== '') {
|
||||
return this.createAndRelateTask(this.query)
|
||||
}
|
||||
|
||||
if (this.newTaskRelationTask.id === 0) {
|
||||
this.$message.error({message: this.$t('task.relation.taskRequired')})
|
||||
return
|
||||
}
|
||||
|
||||
const rel = new TaskRelationModel({
|
||||
taskId: this.taskId,
|
||||
otherTaskId: this.newTaskRelationTask.id,
|
||||
|
|
|
@ -190,10 +190,7 @@ export default {
|
|||
this.$t('task.undoneSuccess'),
|
||||
}, [{
|
||||
title: 'Undo',
|
||||
callback() {
|
||||
this.task.done = !this.task.done
|
||||
this.markAsDone(!checked)
|
||||
},
|
||||
callback: () => this.undoDone(checked),
|
||||
}])
|
||||
}
|
||||
|
||||
|
@ -203,6 +200,11 @@ export default {
|
|||
await updateFunc() // Don't delay it when un-marking it as it doesn't have an animation the other way around
|
||||
}
|
||||
},
|
||||
|
||||
undoDone(checked) {
|
||||
this.task.done = !this.task.done
|
||||
this.markAsDone(!checked)
|
||||
},
|
||||
|
||||
async toggleFavorite() {
|
||||
this.task.isFavorite = !this.task.isFavorite
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {HTTPFactory} from '@/http-common'
|
||||
import {AuthenticatedHTTPFactory} from '@/http-common'
|
||||
import {AxiosResponse} from 'axios'
|
||||
|
||||
let savedToken: string | null = null
|
||||
|
@ -6,8 +6,6 @@ let savedToken: string | null = null
|
|||
/**
|
||||
* Saves a token while optionally saving it to lacal storage. This is used when viewing a link share:
|
||||
* It enables viewing multiple link shares indipendently from each in multiple tabs other without overriding any other open ones.
|
||||
* @param token
|
||||
* @param persist
|
||||
*/
|
||||
export const saveToken = (token: string, persist: boolean) => {
|
||||
savedToken = token
|
||||
|
@ -18,7 +16,6 @@ export const saveToken = (token: string, persist: boolean) => {
|
|||
|
||||
/**
|
||||
* Returns a saved token. If there is one saved in memory it will use that before anything else.
|
||||
* @returns {string|null}
|
||||
*/
|
||||
export const getToken = (): string | null => {
|
||||
if (savedToken !== null) {
|
||||
|
@ -39,16 +36,11 @@ export const removeToken = () => {
|
|||
|
||||
/**
|
||||
* Refreshes an auth token while ensuring it is updated everywhere.
|
||||
* @returns {Promise<AxiosResponse<any>>}
|
||||
*/
|
||||
export async function refreshToken(persist: boolean): Promise<AxiosResponse> {
|
||||
const HTTP = HTTPFactory()
|
||||
const HTTP = AuthenticatedHTTPFactory()
|
||||
try {
|
||||
const response = await HTTP.post('user/token', null, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${getToken()}`,
|
||||
},
|
||||
})
|
||||
const response = await HTTP.post('user/token')
|
||||
saveToken(response.data.token, persist)
|
||||
return response
|
||||
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
import {decode} from 'blurhash'
|
||||
|
||||
export async function getBlobFromBlurHash(blurHash: string): Promise<Blob | null> {
|
||||
if (blurHash === '') {
|
||||
return null
|
||||
}
|
||||
|
||||
const pixels = decode(blurHash, 32, 32)
|
||||
const canvas = document.createElement('canvas')
|
||||
canvas.width = 32
|
||||
canvas.height = 32
|
||||
const ctx = canvas.getContext('2d')
|
||||
if (ctx === null) {
|
||||
return null
|
||||
}
|
||||
|
||||
const imageData = ctx.createImageData(32, 32)
|
||||
imageData.data.set(pixels)
|
||||
ctx.putImageData(imageData, 0, 0)
|
||||
|
||||
return new Promise<Blob>((resolve, reject) => {
|
||||
canvas.toBlob(b => {
|
||||
if (b === null) {
|
||||
reject(b)
|
||||
return
|
||||
}
|
||||
|
||||
resolve(b)
|
||||
})
|
||||
})
|
||||
}
|
|
@ -3,11 +3,15 @@ import popSoundFile from '@/assets/audio/pop.mp3'
|
|||
export const playSoundWhenDoneKey = 'playSoundWhenTaskDone'
|
||||
|
||||
export function playPop() {
|
||||
const enabled = Boolean(localStorage.getItem(playSoundWhenDoneKey))
|
||||
if(!enabled) {
|
||||
const enabled = localStorage.getItem(playSoundWhenDoneKey) === 'true'
|
||||
if (!enabled) {
|
||||
return
|
||||
}
|
||||
|
||||
playPopSound()
|
||||
}
|
||||
|
||||
export function playPopSound() {
|
||||
const popSound = new Audio(popSoundFile)
|
||||
popSound.play()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
export function getNextWeekDate(): Date {
|
||||
return new Date((new Date()).getTime() + 7 * 24 * 60 * 60 * 1000)
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
export function parseDateOrString(rawValue: string | undefined, fallback: any): string | Date {
|
||||
if (typeof rawValue === 'undefined') {
|
||||
return fallback
|
||||
}
|
||||
|
||||
const d = new Date(rawValue)
|
||||
|
||||
// @ts-ignore if rawValue is an invalid date, isNan will return false.
|
||||
return !isNaN(d)
|
||||
? d
|
||||
: rawValue
|
||||
}
|
|
@ -335,6 +335,7 @@
|
|||
"archiveText": "Nebudete moci upravovat tento jmenný prostor ani vytvářet nové seznamy, dokud jej neodarchivujete. Všechny seznamy v tomto prostoru budou také archivovány.",
|
||||
"unarchiveText": "Budete moci vytvářet nové úkoly nebo je upravovat.",
|
||||
"success": "Prostor byl úspěšně archivován.",
|
||||
"unarchiveSuccess": "The namespace was successfully un-archived.",
|
||||
"description": "Pokud je prostor archivován, nelze vytvořit nové seznamy nebo je upravit."
|
||||
},
|
||||
"delete": {
|
||||
|
@ -384,7 +385,7 @@
|
|||
"showDoneTasks": "Zobrazit dokončené úkoly",
|
||||
"sortAlphabetically": "Řadit podle abecedy",
|
||||
"enablePriority": "Povolit filtrování podle priority",
|
||||
"enablePercentDone": "Povolit filtrování dle dokončenosti",
|
||||
"enablePercentDone": "Enable Filter By Progress",
|
||||
"dueDateRange": "Rozsah termínu",
|
||||
"startDateRange": "Začátek období",
|
||||
"endDateRange": "Konec období",
|
||||
|
@ -484,7 +485,8 @@
|
|||
"showMenu": "Zobrazit nabídku",
|
||||
"hideMenu": "Skrýt nabídku",
|
||||
"forExample": "Například:",
|
||||
"welcomeBack": "Vítejte zpět!"
|
||||
"welcomeBack": "Vítejte zpět!",
|
||||
"custom": "Custom"
|
||||
},
|
||||
"input": {
|
||||
"resetColor": "Obnovit barvu",
|
||||
|
@ -523,6 +525,57 @@
|
|||
"multiselect": {
|
||||
"createPlaceholder": "Vytvořit nový",
|
||||
"selectPlaceholder": "Kliknutím nebo stisknutím klávesy Enter vyberte"
|
||||
},
|
||||
"datepickerRange": {
|
||||
"to": "To",
|
||||
"from": "From",
|
||||
"fromto": "{from} to {to}",
|
||||
"ranges": {
|
||||
"today": "Today",
|
||||
"thisWeek": "This Week",
|
||||
"restOfThisWeek": "The Rest of This Week",
|
||||
"nextWeek": "Next Week",
|
||||
"next7Days": "Next 7 Days",
|
||||
"lastWeek": "Last Week",
|
||||
"thisMonth": "This Month",
|
||||
"restOfThisMonth": "The Rest of This Month",
|
||||
"nextMonth": "Next Month",
|
||||
"next30Days": "Next 30 Days",
|
||||
"lastMonth": "Last Month",
|
||||
"thisYear": "This Year",
|
||||
"restOfThisYear": "The Rest of This Year"
|
||||
}
|
||||
},
|
||||
"datemathHelp": {
|
||||
"canuse": "You can use date math to filter for relative dates.",
|
||||
"learnhow": "Check out how it works",
|
||||
"title": "Date Math",
|
||||
"intro": "Date Math allows you to specifiy relative dates which are resolved on the fly by Vikunja when applying the filter.",
|
||||
"expression": "Each Date Math expression starts with an anchor date, which can either be {0}, or a date string ending with {1}. This anchor date can optionally be followed by one or more maths expressions.",
|
||||
"similar": "These expressions are similar to the ones provided by {0} and {1}.",
|
||||
"add1Day": "Add one day",
|
||||
"minus1Day": "Subtract one day",
|
||||
"roundDay": "Round down to the nearest day",
|
||||
"supportedUnits": "Supported time units are:",
|
||||
"someExamples": "Some examples of time expressions:",
|
||||
"units": {
|
||||
"seconds": "Seconds",
|
||||
"minutes": "Minutes",
|
||||
"hours": "Hours",
|
||||
"days": "Days",
|
||||
"weeks": "Weeks",
|
||||
"months": "Months",
|
||||
"years": "Years"
|
||||
},
|
||||
"examples": {
|
||||
"now": "Right now",
|
||||
"in24h": "In 24h",
|
||||
"today": "Today at 00:00",
|
||||
"beginningOfThisWeek": "The beginning of this week at 00:00",
|
||||
"endOfThisWeek": "The end of this week",
|
||||
"in30Days": "In 30 days",
|
||||
"datePlusMonth": "{0} plus one month at 00:00 of that day"
|
||||
}
|
||||
}
|
||||
},
|
||||
"task": {
|
||||
|
@ -540,12 +593,9 @@
|
|||
"titleCurrent": "Aktuální úkoly",
|
||||
"titleDates": "Úkoly od {from} do {to}",
|
||||
"noDates": "Zobrazit úkoly bez datumu",
|
||||
"current": "Aktuální úkoly",
|
||||
"from": "Úkoly od",
|
||||
"until": "do",
|
||||
"today": "Dnes",
|
||||
"nextWeek": "Příští týden",
|
||||
"nextMonth": "Příští měsíc",
|
||||
"overdue": "Show overdue tasks",
|
||||
"fromuntil": "Tasks from {from} until {until}",
|
||||
"select": "Select a date range",
|
||||
"noTasks": "Nic na práci - užij si pěkný den!"
|
||||
},
|
||||
"detail": {
|
||||
|
@ -569,22 +619,22 @@
|
|||
"text2": "Tímto také odstraníte všechny přílohy, připomenutí a vztahy spojené s tímto úkolem a nelze je vrátit zpět!"
|
||||
},
|
||||
"actions": {
|
||||
"assign": "Přiřadit uživateli",
|
||||
"label": "Přidat štítky",
|
||||
"assign": "Assign to User",
|
||||
"label": "Add Labels",
|
||||
"priority": "Nastavit prioritu",
|
||||
"dueDate": "Nastavit termín",
|
||||
"startDate": "Nastavit počáteční datum",
|
||||
"endDate": "Nastavit koncové datum",
|
||||
"startDate": "Set Start Date",
|
||||
"endDate": "Set End Date",
|
||||
"reminders": "Nastavit připomenutí",
|
||||
"repeatAfter": "Nastavit interval opakování",
|
||||
"percentDone": "Nastavit procenta dokončeno",
|
||||
"attachments": "Přidat přílohy",
|
||||
"relatedTasks": "Přidat vztahy úkolu",
|
||||
"moveList": "Přesunout úkol",
|
||||
"color": "Nastavit barvu úkolu",
|
||||
"delete": "Smazat úkol",
|
||||
"favorite": "Uložit jako oblíbené",
|
||||
"unfavorite": "Odebrat z oblíbených"
|
||||
"repeatAfter": "Set Repeating Interval",
|
||||
"percentDone": "Set Progress",
|
||||
"attachments": "Add Attachments",
|
||||
"relatedTasks": "Add Relation",
|
||||
"moveList": "Move",
|
||||
"color": "Set Color",
|
||||
"delete": "Delete",
|
||||
"favorite": "Add to Favorites",
|
||||
"unfavorite": "Remove from Favorites"
|
||||
}
|
||||
},
|
||||
"attributes": {
|
||||
|
@ -597,7 +647,7 @@
|
|||
"dueDate": "Termín",
|
||||
"endDate": "Datum ukončení",
|
||||
"labels": "Štítky",
|
||||
"percentDone": "% Hotovo",
|
||||
"percentDone": "Progress",
|
||||
"priority": "Priorita",
|
||||
"relatedTasks": "Související úkoly",
|
||||
"reminders": "Připomínky",
|
||||
|
@ -784,17 +834,20 @@
|
|||
"general": "Obecné",
|
||||
"allPages": "Tyto zkratky fungují na všech stránkách.",
|
||||
"currentPageOnly": "Tyto zkratky fungují pouze na aktuální stránce.",
|
||||
"somePagesOnly": "These shortcuts work only on some pages.",
|
||||
"toggleMenu": "Přepnout nabídku",
|
||||
"quickSearch": "Otevřít vyhledávání / panel rychlých akcí",
|
||||
"then": "potom",
|
||||
"task": {
|
||||
"title": "Stránka úkolů",
|
||||
"done": "Hotovo",
|
||||
"assign": "Přiřadit uživateli",
|
||||
"done": "Mark task done / undone",
|
||||
"assign": "Assign this task to a user",
|
||||
"labels": "Přidat štítky k tomuto úkolu",
|
||||
"dueDate": "Změnit termín tohoto úkolu",
|
||||
"attachment": "Přidat přílohu k tomuto úkolu",
|
||||
"related": "Upravit související úkoly tohoto úkolu"
|
||||
"related": "Upravit související úkoly tohoto úkolu",
|
||||
"color": "Change the color of this task",
|
||||
"move": "Move this task to another list"
|
||||
},
|
||||
"list": {
|
||||
"title": "Zobrazení seznamů",
|
||||
|
@ -802,6 +855,14 @@
|
|||
"switchToGanttView": "Přepnout na zobrazení gantt",
|
||||
"switchToKanbanView": "Přepnout na zobrazení kanbanu",
|
||||
"switchToTableView": "Přepnout na zobrazení tabulky"
|
||||
},
|
||||
"navigation": {
|
||||
"title": "Navigation",
|
||||
"overview": "Navigato to overview",
|
||||
"upcoming": "Navigato to upcoming taks",
|
||||
"namespaces": "Navigate to namepaces & lists",
|
||||
"labels": "Navigate to labels",
|
||||
"teams": "Navigate to teams"
|
||||
}
|
||||
},
|
||||
"update": {
|
||||
|
|
|
@ -57,7 +57,7 @@
|
|||
"hidePassword": "Passwort verbergen",
|
||||
"noAccountYet": "Noch kein Account?",
|
||||
"alreadyHaveAnAccount": "Hast du bereits einen Account?",
|
||||
"remember": "Stay logged in"
|
||||
"remember": "Angemeldet bleiben"
|
||||
},
|
||||
"settings": {
|
||||
"title": "Einstellungen",
|
||||
|
@ -86,7 +86,7 @@
|
|||
"weekStartMonday": "Montag",
|
||||
"language": "Sprache",
|
||||
"defaultList": "Standard-Liste",
|
||||
"timezone": "Time Zone"
|
||||
"timezone": "Zeitzone"
|
||||
},
|
||||
"totp": {
|
||||
"title": "Zwei-Faktor-Authentifizierung",
|
||||
|
@ -335,7 +335,7 @@
|
|||
"archiveText": "Du kannst diesen Namespace nicht mehr bearbeiten oder neue Listen erstellen, bis du die Archivierung rückgängig machst. Das gilt auch für alle Listen in diesem Namespace.",
|
||||
"unarchiveText": "Du kannst neue Aufgaben erstellen oder diese bearbeiten.",
|
||||
"success": "Der Namespace wurde erfolgreich archiviert.",
|
||||
"unarchiveSuccess": "The namespace was successfully un-archived.",
|
||||
"unarchiveSuccess": "Der Namespace wurde erfolgreich wiederhergestellt.",
|
||||
"description": "In einem archivierten Namespace können Listen weder angelegt noch editiert werden."
|
||||
},
|
||||
"delete": {
|
||||
|
@ -385,7 +385,7 @@
|
|||
"showDoneTasks": "Erledigte Aufgaben anzeigen",
|
||||
"sortAlphabetically": "Alphabetisch sortieren",
|
||||
"enablePriority": "Filter nach Priorität aktivieren",
|
||||
"enablePercentDone": "Filter nach % Erledigt aktivieren",
|
||||
"enablePercentDone": "Filter nach Fortschritt aktivieren",
|
||||
"dueDateRange": "Fälligkeitsbereich",
|
||||
"startDateRange": "Startdatumsbereich",
|
||||
"endDateRange": "Enddatumsbereich",
|
||||
|
@ -485,7 +485,8 @@
|
|||
"showMenu": "Menü anzeigen",
|
||||
"hideMenu": "Menü ausblenden",
|
||||
"forExample": "Zum Beispiel:",
|
||||
"welcomeBack": "Willkommen zurück!"
|
||||
"welcomeBack": "Willkommen zurück!",
|
||||
"custom": "Benutzerdefiniert"
|
||||
},
|
||||
"input": {
|
||||
"resetColor": "Farbe zurücksetzen",
|
||||
|
@ -524,6 +525,57 @@
|
|||
"multiselect": {
|
||||
"createPlaceholder": "Neu erstellen",
|
||||
"selectPlaceholder": "Klicken oder Enter drücken zum Auswählen"
|
||||
},
|
||||
"datepickerRange": {
|
||||
"to": "Bis",
|
||||
"from": "Von",
|
||||
"fromto": "{from} bis {to}",
|
||||
"ranges": {
|
||||
"today": "Heute",
|
||||
"thisWeek": "Diese Woche",
|
||||
"restOfThisWeek": "Der Rest dieser Woche",
|
||||
"nextWeek": "Nächste Woche",
|
||||
"next7Days": "Nächsten 7 Tage",
|
||||
"lastWeek": "Letzte Woche",
|
||||
"thisMonth": "Dieser Monat",
|
||||
"restOfThisMonth": "Der Rest dieses Monats",
|
||||
"nextMonth": "Nächster Monat",
|
||||
"next30Days": "Nächsten 30 Tage",
|
||||
"lastMonth": "Letzter Monat",
|
||||
"thisYear": "Dieses Jahr",
|
||||
"restOfThisYear": "Der Rest des Jahres"
|
||||
}
|
||||
},
|
||||
"datemathHelp": {
|
||||
"canuse": "Du kannst Datumsberechnung verwenden, um nach relativen Daten zu filtern.",
|
||||
"learnhow": "Sieh dir an, wie es funktioniert",
|
||||
"title": "Datumsberechnung",
|
||||
"intro": "Die Datumsberechnung erlaubt es, relative Daten anzugeben, die bei der Anwendung des Filters von Vikunja aufgelöst werden.",
|
||||
"expression": "Jeder Ausdruck der Datumsberechnung beginnt mit einem Datumswert, welcher entweder {0} sein kann oder mit {1} endet. Auf diesen Datumswert kann optional ein oder mehrere mathematische Ausdrücke folgen.",
|
||||
"similar": "Diese Ausdrücke ähneln denen von {0} und {1}.",
|
||||
"add1Day": "Einen Tag hinzufügen",
|
||||
"minus1Day": "Einen Tag abziehen",
|
||||
"roundDay": "Auf den nächsten Tag abrunden",
|
||||
"supportedUnits": "Unterstützte Zeiteinheiten sind:",
|
||||
"someExamples": "Einige Beispiele für Zeitausdrücke:",
|
||||
"units": {
|
||||
"seconds": "Sekunden",
|
||||
"minutes": "Minuten",
|
||||
"hours": "Stunden",
|
||||
"days": "Tage",
|
||||
"weeks": "Wochen",
|
||||
"months": "Monate",
|
||||
"years": "Jahre"
|
||||
},
|
||||
"examples": {
|
||||
"now": "Jetzt",
|
||||
"in24h": "In 24 Stunden",
|
||||
"today": "Heute um 00:00 Uhr",
|
||||
"beginningOfThisWeek": "Der Anfang dieser Woche um 00:00 Uhr",
|
||||
"endOfThisWeek": "Das Ende dieser Woche",
|
||||
"in30Days": "In 30 Tagen",
|
||||
"datePlusMonth": "{0} plus einen Monat um 00:00 des Tages"
|
||||
}
|
||||
}
|
||||
},
|
||||
"task": {
|
||||
|
@ -541,12 +593,9 @@
|
|||
"titleCurrent": "Aktuelle Aufgaben",
|
||||
"titleDates": "Aufgaben von {from} bis {to}",
|
||||
"noDates": "Aufgaben ohne Datum anzeigen",
|
||||
"current": "Aktuelle Aufgaben",
|
||||
"from": "Aufgaben von",
|
||||
"until": "bis",
|
||||
"today": "Heute",
|
||||
"nextWeek": "Nächste Woche",
|
||||
"nextMonth": "Nächster Monat",
|
||||
"overdue": "Überfällige Aufgaben anzeigen",
|
||||
"fromuntil": "Aufgaben von {from} bis {until}",
|
||||
"select": "Datumsbereich wählen",
|
||||
"noTasks": "Nichts zu tun – Einen schönen Tag noch!"
|
||||
},
|
||||
"detail": {
|
||||
|
@ -578,13 +627,13 @@
|
|||
"endDate": "Enddatum setzen",
|
||||
"reminders": "Erinnerungen setzen",
|
||||
"repeatAfter": "Wiederholung setzen",
|
||||
"percentDone": "Prozent erledigt setzen",
|
||||
"percentDone": "Fortschritt einstellen",
|
||||
"attachments": "Anhänge hinzufügen",
|
||||
"relatedTasks": "Aufgabenbeziehungen hinzufügen",
|
||||
"moveList": "Aufgabe verschieben",
|
||||
"color": "Taskfarbe setzen",
|
||||
"delete": "Aufgabe löschen",
|
||||
"favorite": "In Favoriten speichern",
|
||||
"relatedTasks": "Beziehung hinzufügen",
|
||||
"moveList": "Verschieben",
|
||||
"color": "Farbe setzen",
|
||||
"delete": "Löschen",
|
||||
"favorite": "Zu Favoriten hinzufügen",
|
||||
"unfavorite": "Aus Favoriten entfernen"
|
||||
}
|
||||
},
|
||||
|
@ -598,7 +647,7 @@
|
|||
"dueDate": "Fälligkeitsdatum",
|
||||
"endDate": "Enddatum",
|
||||
"labels": "Labels",
|
||||
"percentDone": "% erledigt",
|
||||
"percentDone": "Fortschritt",
|
||||
"priority": "Priorität",
|
||||
"relatedTasks": "Verwandte Aufgaben",
|
||||
"reminders": "Erinnerungen",
|
||||
|
@ -785,17 +834,20 @@
|
|||
"general": "Allgemein",
|
||||
"allPages": "Diese Tastenkürzel funktionieren auf allen Seiten.",
|
||||
"currentPageOnly": "Diese Tastenkürzel funktionieren nur auf der aktuellen Seite.",
|
||||
"somePagesOnly": "Funktioniert nur auf manchen Seiten.",
|
||||
"toggleMenu": "Das Menü umschalten",
|
||||
"quickSearch": "Such-/Schnellaktionsleiste öffnen",
|
||||
"then": "dann",
|
||||
"task": {
|
||||
"title": "Aufgabenseite",
|
||||
"done": "Fertig",
|
||||
"assign": "Benutzer:in zuweisen",
|
||||
"done": "Aufgabe als erledigt / unerledigt markieren",
|
||||
"assign": "Diese Aufgabe jemandem zuweisen",
|
||||
"labels": "Dieser Aufgabe ein Label hinzufügen",
|
||||
"dueDate": "Ändere das Fälligkeitsdatum dieser Aufgabe",
|
||||
"attachment": "Einen Anhang dieser Aufgabe hinzufügen",
|
||||
"related": "Ändere die Abhängigen Aufgaben dieser Aufgabe"
|
||||
"related": "Ändere die Abhängigen Aufgaben dieser Aufgabe",
|
||||
"color": "Die Farbe dieser Aufgabe ändern",
|
||||
"move": "Diese Aufgabe in eine andere Liste verschieben"
|
||||
},
|
||||
"list": {
|
||||
"title": "Listenansicht",
|
||||
|
@ -803,6 +855,14 @@
|
|||
"switchToGanttView": "Zur Ganttansicht wechseln",
|
||||
"switchToKanbanView": "Zur Kanbanansicht wechseln",
|
||||
"switchToTableView": "Zur Tabellenansicht wechseln"
|
||||
},
|
||||
"navigation": {
|
||||
"title": "Navigation",
|
||||
"overview": "Die Startseite aufrufen",
|
||||
"upcoming": "Anstehende Aufgaben aufrufen",
|
||||
"namespaces": "Namespaces & Listen aufrufen",
|
||||
"labels": "Labels aufrufen",
|
||||
"teams": "Teams aufrufen"
|
||||
}
|
||||
},
|
||||
"update": {
|
||||
|
|
|
@ -57,7 +57,7 @@
|
|||
"hidePassword": "Passwort verbergen",
|
||||
"noAccountYet": "Noch kein Account?",
|
||||
"alreadyHaveAnAccount": "Hast du bereits einen Account?",
|
||||
"remember": "Stay logged in"
|
||||
"remember": "Angemeldet bleiben"
|
||||
},
|
||||
"settings": {
|
||||
"title": "Iihstellige",
|
||||
|
@ -86,7 +86,7 @@
|
|||
"weekStartMonday": "Määntig",
|
||||
"language": "Sproch",
|
||||
"defaultList": "Standard Liste",
|
||||
"timezone": "Time Zone"
|
||||
"timezone": "Zeitzone"
|
||||
},
|
||||
"totp": {
|
||||
"title": "Zweifaktor Authentifizierig",
|
||||
|
@ -103,9 +103,9 @@
|
|||
"disableSuccess": "Zweifaktor Authentifizierig isch erfolgriich uusgschalte wore."
|
||||
},
|
||||
"caldav": {
|
||||
"title": "Caldav",
|
||||
"howTo": "Du chasch Vikunja zu Caldav Applikatione verbinde, um dini Uufgabe vo verschidene Gräät zgseh. Gib die Url i dim Client iih:",
|
||||
"more": "Meh Informatione über Caldav in Vikunja"
|
||||
"title": "CalDAV",
|
||||
"howTo": "Du chasch Vikunja zu CalDAV Applikatione verbinde, um dini Uufgabe vo verschidene Gräät zgseh. Gib die Url i dim Client iih:",
|
||||
"more": "Meh Informatione über CalDAV in Vikunja"
|
||||
},
|
||||
"avatar": {
|
||||
"title": "Herr Der Elemente",
|
||||
|
@ -335,7 +335,7 @@
|
|||
"archiveText": "Du hesch kei möglichkeit meh de Namensruum z'bearbeite oder neui Listene drin z'erstelle, bis du si wider ent-archiviert hesch. Das archiviert au grad alli Liste im Namensruum.",
|
||||
"unarchiveText": "Du chasch neui Liste erstelle oder bearbeite.",
|
||||
"success": "De Namensruum isch erfolgriich archiviert worde.",
|
||||
"unarchiveSuccess": "The namespace was successfully un-archived.",
|
||||
"unarchiveSuccess": "Der Namespace wurde erfolgreich wiederhergestellt.",
|
||||
"description": "Wenn en Namensruum archiviert isch, chasch du kei neui Liste erstelle oder die bearbeite."
|
||||
},
|
||||
"delete": {
|
||||
|
@ -385,7 +385,7 @@
|
|||
"showDoneTasks": "Zeig die fertige Uufgabe",
|
||||
"sortAlphabetically": "Alphabetisch sortieren",
|
||||
"enablePriority": "Filter nach Priorität aktiviere",
|
||||
"enablePercentDone": "Filter nach Prozent iihschalte",
|
||||
"enablePercentDone": "Filter nach Fortschritt aktivieren",
|
||||
"dueDateRange": "Fälligkeitsberiich",
|
||||
"startDateRange": "Startdatumsbreiich",
|
||||
"endDateRange": "Enddatumsberiich",
|
||||
|
@ -485,7 +485,8 @@
|
|||
"showMenu": "Menü anzeigen",
|
||||
"hideMenu": "Menü ausblenden",
|
||||
"forExample": "Zum Beispiel:",
|
||||
"welcomeBack": "Willkommen zurück!"
|
||||
"welcomeBack": "Willkommen zurück!",
|
||||
"custom": "Benutzerdefiniert"
|
||||
},
|
||||
"input": {
|
||||
"resetColor": "Farb zruggsetze",
|
||||
|
@ -524,6 +525,57 @@
|
|||
"multiselect": {
|
||||
"createPlaceholder": "Neu erstelle",
|
||||
"selectPlaceholder": "Druck uf Enter zum uuswähle"
|
||||
},
|
||||
"datepickerRange": {
|
||||
"to": "Bis",
|
||||
"from": "Von",
|
||||
"fromto": "{from} bis {to}",
|
||||
"ranges": {
|
||||
"today": "Heute",
|
||||
"thisWeek": "Diese Woche",
|
||||
"restOfThisWeek": "Der Rest dieser Woche",
|
||||
"nextWeek": "Nächste Woche",
|
||||
"next7Days": "Nächsten 7 Tage",
|
||||
"lastWeek": "Letzte Woche",
|
||||
"thisMonth": "Dieser Monat",
|
||||
"restOfThisMonth": "Der Rest dieses Monats",
|
||||
"nextMonth": "Nächster Monat",
|
||||
"next30Days": "Nächsten 30 Tage",
|
||||
"lastMonth": "Letzter Monat",
|
||||
"thisYear": "Dieses Jahr",
|
||||
"restOfThisYear": "Der Rest des Jahres"
|
||||
}
|
||||
},
|
||||
"datemathHelp": {
|
||||
"canuse": "Du kannst Datumsberechnung verwenden, um nach relativen Daten zu filtern.",
|
||||
"learnhow": "Sieh dir an, wie es funktioniert",
|
||||
"title": "Datumsberechnung",
|
||||
"intro": "Die Datumsberechnung erlaubt es, relative Daten anzugeben, die bei der Anwendung des Filters von Vikunja aufgelöst werden.",
|
||||
"expression": "Jeder Ausdruck der Datumsberechnung beginnt mit einem Datumswert, welcher entweder {0} sein kann oder mit {1} endet. Auf diesen Datumswert kann optional ein oder mehrere mathematische Ausdrücke folgen.",
|
||||
"similar": "Diese Ausdrücke ähneln denen von {0} und {1}.",
|
||||
"add1Day": "Einen Tag hinzufügen",
|
||||
"minus1Day": "Einen Tag abziehen",
|
||||
"roundDay": "Auf den nächsten Tag abrunden",
|
||||
"supportedUnits": "Unterstützte Zeiteinheiten sind:",
|
||||
"someExamples": "Einige Beispiele für Zeitausdrücke:",
|
||||
"units": {
|
||||
"seconds": "Sekunden",
|
||||
"minutes": "Minuten",
|
||||
"hours": "Stunden",
|
||||
"days": "Tage",
|
||||
"weeks": "Wochen",
|
||||
"months": "Monate",
|
||||
"years": "Jahre"
|
||||
},
|
||||
"examples": {
|
||||
"now": "Jetzt",
|
||||
"in24h": "In 24 Stunden",
|
||||
"today": "Heute um 00:00 Uhr",
|
||||
"beginningOfThisWeek": "Der Anfang dieser Woche um 00:00 Uhr",
|
||||
"endOfThisWeek": "Das Ende dieser Woche",
|
||||
"in30Days": "In 30 Tagen",
|
||||
"datePlusMonth": "{0} plus einen Monat um 00:00 des Tages"
|
||||
}
|
||||
}
|
||||
},
|
||||
"task": {
|
||||
|
@ -541,12 +593,9 @@
|
|||
"titleCurrent": "Momentani Uufgabe",
|
||||
"titleDates": "Uufgabe vo {from} bis {to}",
|
||||
"noDates": "Zeig Uufgabe ohni Datum",
|
||||
"current": "Momentani Uufgabe",
|
||||
"from": "Uufgabe vo",
|
||||
"until": "bis",
|
||||
"today": "Hütt",
|
||||
"nextWeek": "Negst Wuchä",
|
||||
"nextMonth": "Negste Monet",
|
||||
"overdue": "Überfällige Aufgaben anzeigen",
|
||||
"fromuntil": "Aufgaben von {from} bis {until}",
|
||||
"select": "Datumsbereich wählen",
|
||||
"noTasks": "Nichts zu tun – Einen schönen Tag noch!"
|
||||
},
|
||||
"detail": {
|
||||
|
@ -571,20 +620,20 @@
|
|||
},
|
||||
"actions": {
|
||||
"assign": "Benutzer:in zuweisen",
|
||||
"label": "Label hinzuefüege",
|
||||
"label": "Label hinzufügen",
|
||||
"priority": "Priorität setzä",
|
||||
"dueDate": "Fälligkeitsdatum setze",
|
||||
"startDate": "Startdatum setze",
|
||||
"endDate": "Enddatum setze",
|
||||
"startDate": "Startdatum setzen",
|
||||
"endDate": "Enddatum setzen",
|
||||
"reminders": "Errinnerig iihstelle",
|
||||
"repeatAfter": "En wiederholende Intervall setze",
|
||||
"percentDone": "Prozentuelli Erledigung setze",
|
||||
"attachments": "Aahang hinzuefüege",
|
||||
"relatedTasks": "Uufgabsbeziehig hinzufüege",
|
||||
"moveList": "Uufgab verschiebe",
|
||||
"color": "Uufgab Farb setze",
|
||||
"delete": "Uufgab chüble",
|
||||
"favorite": "In Favoriten speichern",
|
||||
"repeatAfter": "Wiederholung setzen",
|
||||
"percentDone": "Fortschritt einstellen",
|
||||
"attachments": "Anhänge hinzufügen",
|
||||
"relatedTasks": "Beziehung hinzufügen",
|
||||
"moveList": "Verschieben",
|
||||
"color": "Farbe setzen",
|
||||
"delete": "Löschen",
|
||||
"favorite": "Zu Favoriten hinzufügen",
|
||||
"unfavorite": "Aus Favoriten entfernen"
|
||||
}
|
||||
},
|
||||
|
@ -598,7 +647,7 @@
|
|||
"dueDate": "Fälligkeitsdatum",
|
||||
"endDate": "Enddatum",
|
||||
"labels": "Labels",
|
||||
"percentDone": "% fertig",
|
||||
"percentDone": "Fortschritt",
|
||||
"priority": "Priorität",
|
||||
"relatedTasks": "Verwandti Uufgabe",
|
||||
"reminders": "Errinnerige",
|
||||
|
@ -785,17 +834,20 @@
|
|||
"general": "Allgemein",
|
||||
"allPages": "Die Chürzl funktioniered uf allne Siitene.",
|
||||
"currentPageOnly": "Die Chürzl funktioniered nur uf de momentane Siite.",
|
||||
"somePagesOnly": "Funktioniert nur auf manchen Seiten.",
|
||||
"toggleMenu": "Menü umschalte",
|
||||
"quickSearch": "Suechi und Schnellaktionsliste öffne",
|
||||
"then": "dann",
|
||||
"task": {
|
||||
"title": "Uufgabesiite",
|
||||
"done": "Fertig",
|
||||
"assign": "Benutzer:in zuweisen",
|
||||
"done": "Aufgabe als erledigt / unerledigt markieren",
|
||||
"assign": "Diese Aufgabe jemandem zuweisen",
|
||||
"labels": "Labels ennere Uufgab hinzuefüege",
|
||||
"dueDate": "S'Fälligkeitsdatum für die Uufgab ändere",
|
||||
"attachment": "En Aahang dere Uufgab hinzuefüege",
|
||||
"related": "Beziehige vo dere Uufgab bearbeite"
|
||||
"related": "Beziehige vo dere Uufgab bearbeite",
|
||||
"color": "Die Farbe dieser Aufgabe ändern",
|
||||
"move": "Diese Aufgabe in eine andere Liste verschieben"
|
||||
},
|
||||
"list": {
|
||||
"title": "Listenansicht",
|
||||
|
@ -803,6 +855,14 @@
|
|||
"switchToGanttView": "Zur Ganttansicht wechseln",
|
||||
"switchToKanbanView": "Zur Kanbanansicht wechseln",
|
||||
"switchToTableView": "Zur Tabellenansicht wechseln"
|
||||
},
|
||||
"navigation": {
|
||||
"title": "Navigation",
|
||||
"overview": "Die Startseite aufrufen",
|
||||
"upcoming": "Anstehende Aufgaben aufrufen",
|
||||
"namespaces": "Namespaces & Listen aufrufen",
|
||||
"labels": "Labels aufrufen",
|
||||
"teams": "Teams aufrufen"
|
||||
}
|
||||
},
|
||||
"update": {
|
||||
|
|
|
@ -392,7 +392,7 @@
|
|||
"showDoneTasks": "Show Done Tasks",
|
||||
"sortAlphabetically": "Sort Alphabetically",
|
||||
"enablePriority": "Enable Filter By Priority",
|
||||
"enablePercentDone": "Enable Filter By Percent Done",
|
||||
"enablePercentDone": "Enable Filter By Progress",
|
||||
"dueDateRange": "Due Date Range",
|
||||
"startDateRange": "Start Date Range",
|
||||
"endDateRange": "End Date Range",
|
||||
|
@ -493,6 +493,7 @@
|
|||
"hideMenu": "Hide the menu",
|
||||
"forExample": "For example:",
|
||||
"welcomeBack": "Welcome Back!",
|
||||
"custom": "Custom",
|
||||
"id": "ID",
|
||||
"created": "Created at",
|
||||
"actions": "Actions"
|
||||
|
@ -534,6 +535,60 @@
|
|||
"multiselect": {
|
||||
"createPlaceholder": "Create new",
|
||||
"selectPlaceholder": "Click or press enter to select"
|
||||
},
|
||||
"datepickerRange": {
|
||||
"to": "To",
|
||||
"from": "From",
|
||||
"fromto": "{from} to {to}",
|
||||
"ranges": {
|
||||
"today": "Today",
|
||||
|
||||
"thisWeek": "This Week",
|
||||
"restOfThisWeek": "The Rest of This Week",
|
||||
"nextWeek": "Next Week",
|
||||
"next7Days": "Next 7 Days",
|
||||
"lastWeek": "Last Week",
|
||||
|
||||
"thisMonth": "This Month",
|
||||
"restOfThisMonth": "The Rest of This Month",
|
||||
"nextMonth": "Next Month",
|
||||
"next30Days": "Next 30 Days",
|
||||
"lastMonth": "Last Month",
|
||||
|
||||
"thisYear": "This Year",
|
||||
"restOfThisYear": "The Rest of This Year"
|
||||
}
|
||||
},
|
||||
"datemathHelp": {
|
||||
"canuse": "You can use date math to filter for relative dates.",
|
||||
"learnhow": "Check out how it works",
|
||||
"title": "Date Math",
|
||||
"intro": "Date Math allows you to specifiy relative dates which are resolved on the fly by Vikunja when applying the filter.",
|
||||
"expression": "Each Date Math expression starts with an anchor date, which can either be {0}, or a date string ending with {1}. This anchor date can optionally be followed by one or more maths expressions.",
|
||||
"similar": "These expressions are similar to the ones provided by {0} and {1}.",
|
||||
"add1Day": "Add one day",
|
||||
"minus1Day": "Subtract one day",
|
||||
"roundDay": "Round down to the nearest day",
|
||||
"supportedUnits": "Supported time units are:",
|
||||
"someExamples": "Some examples of time expressions:",
|
||||
"units": {
|
||||
"seconds": "Seconds",
|
||||
"minutes": "Minutes",
|
||||
"hours": "Hours",
|
||||
"days": "Days",
|
||||
"weeks": "Weeks",
|
||||
"months": "Months",
|
||||
"years": "Years"
|
||||
},
|
||||
"examples": {
|
||||
"now": "Right now",
|
||||
"in24h": "In 24h",
|
||||
"today": "Today at 00:00",
|
||||
"beginningOfThisWeek": "The beginning of this week at 00:00",
|
||||
"endOfThisWeek": "The end of this week",
|
||||
"in30Days": "In 30 days",
|
||||
"datePlusMonth": "{0} plus one month at 00:00 of that day"
|
||||
}
|
||||
}
|
||||
},
|
||||
"task": {
|
||||
|
@ -551,12 +606,9 @@
|
|||
"titleCurrent": "Current Tasks",
|
||||
"titleDates": "Tasks from {from} until {to}",
|
||||
"noDates": "Show tasks without dates",
|
||||
"current": "Current tasks",
|
||||
"from": "Tasks from",
|
||||
"until": "until",
|
||||
"today": "Today",
|
||||
"nextWeek": "Next Week",
|
||||
"nextMonth": "Next Month",
|
||||
"overdue": "Show overdue tasks",
|
||||
"fromuntil": "Tasks from {from} until {until}",
|
||||
"select": "Select a date range",
|
||||
"noTasks": "Nothing to do — Have a nice day!"
|
||||
},
|
||||
"detail": {
|
||||
|
@ -580,22 +632,22 @@
|
|||
"text2": "This will also remove all attachments, reminders and relations associated with this task and cannot be undone!"
|
||||
},
|
||||
"actions": {
|
||||
"assign": "Assign to a user",
|
||||
"label": "Add labels",
|
||||
"assign": "Assign to User",
|
||||
"label": "Add Labels",
|
||||
"priority": "Set Priority",
|
||||
"dueDate": "Set Due Date",
|
||||
"startDate": "Set a Start Date",
|
||||
"endDate": "Set an End Date",
|
||||
"startDate": "Set Start Date",
|
||||
"endDate": "Set End Date",
|
||||
"reminders": "Set Reminders",
|
||||
"repeatAfter": "Set a repeating interval",
|
||||
"percentDone": "Set Percent Done",
|
||||
"attachments": "Add attachments",
|
||||
"relatedTasks": "Add task relations",
|
||||
"moveList": "Move task",
|
||||
"color": "Set task color",
|
||||
"delete": "Delete task",
|
||||
"favorite": "Save as favorite",
|
||||
"unfavorite": "Remove from favorites"
|
||||
"repeatAfter": "Set Repeating Interval",
|
||||
"percentDone": "Set Progress",
|
||||
"attachments": "Add Attachments",
|
||||
"relatedTasks": "Add Relation",
|
||||
"moveList": "Move",
|
||||
"color": "Set Color",
|
||||
"delete": "Delete",
|
||||
"favorite": "Add to Favorites",
|
||||
"unfavorite": "Remove from Favorites"
|
||||
}
|
||||
},
|
||||
"attributes": {
|
||||
|
@ -608,7 +660,7 @@
|
|||
"dueDate": "Due Date",
|
||||
"endDate": "End Date",
|
||||
"labels": "Labels",
|
||||
"percentDone": "% Done",
|
||||
"percentDone": "Progress",
|
||||
"priority": "Priority",
|
||||
"relatedTasks": "Related Tasks",
|
||||
"reminders": "Reminders",
|
||||
|
@ -695,6 +747,7 @@
|
|||
"deleteText1": "Are you sure you want to delete this task relation?",
|
||||
"deleteText2": "This cannot be undone!",
|
||||
"select": "Select a relation kind",
|
||||
"taskRequired": "Please select a task or enter a new task title.",
|
||||
"kinds": {
|
||||
"subtask": "Subtask | Subtasks",
|
||||
"parenttask": "Parent Task | Parent Tasks",
|
||||
|
@ -795,17 +848,20 @@
|
|||
"general": "General",
|
||||
"allPages": "These shortcuts work on all pages.",
|
||||
"currentPageOnly": "These shortcuts work only on the current page.",
|
||||
"somePagesOnly": "These shortcuts work only on some pages.",
|
||||
"toggleMenu": "Toggle The Menu",
|
||||
"quickSearch": "Open the search/quick action bar",
|
||||
"then": "then",
|
||||
"task": {
|
||||
"title": "Task Page",
|
||||
"done": "Done",
|
||||
"assign": "Assign to a user",
|
||||
"done": "Mark task done / undone",
|
||||
"assign": "Assign this task to a user",
|
||||
"labels": "Add labels to this task",
|
||||
"dueDate": "Change the due date of this task",
|
||||
"attachment": "Add an attachment to this task",
|
||||
"related": "Modify related tasks of this task"
|
||||
"related": "Modify related tasks of this task",
|
||||
"color": "Change the color of this task",
|
||||
"move": "Move this task to another list"
|
||||
},
|
||||
"list": {
|
||||
"title": "List Views",
|
||||
|
@ -813,6 +869,14 @@
|
|||
"switchToGanttView": "Switch to gantt view",
|
||||
"switchToKanbanView": "Switch to kanban view",
|
||||
"switchToTableView": "Switch to table view"
|
||||
},
|
||||
"navigation": {
|
||||
"title": "Navigation",
|
||||
"overview": "Navigato to overview",
|
||||
"upcoming": "Navigato to upcoming taks",
|
||||
"namespaces": "Navigate to namepaces & lists",
|
||||
"labels": "Navigate to labels",
|
||||
"teams": "Navigate to teams"
|
||||
}
|
||||
},
|
||||
"update": {
|
||||
|
|
|
@ -385,7 +385,7 @@
|
|||
"showDoneTasks": "Show Done Tasks",
|
||||
"sortAlphabetically": "Sort Alphabetically",
|
||||
"enablePriority": "Enable Filter By Priority",
|
||||
"enablePercentDone": "Enable Filter By Percent Done",
|
||||
"enablePercentDone": "Enable Filter By Progress",
|
||||
"dueDateRange": "Due Date Range",
|
||||
"startDateRange": "Start Date Range",
|
||||
"endDateRange": "End Date Range",
|
||||
|
@ -485,7 +485,8 @@
|
|||
"showMenu": "Show the menu",
|
||||
"hideMenu": "Hide the menu",
|
||||
"forExample": "For example:",
|
||||
"welcomeBack": "Welcome Back!"
|
||||
"welcomeBack": "Welcome Back!",
|
||||
"custom": "Custom"
|
||||
},
|
||||
"input": {
|
||||
"resetColor": "Reset Color",
|
||||
|
@ -524,6 +525,57 @@
|
|||
"multiselect": {
|
||||
"createPlaceholder": "Create new",
|
||||
"selectPlaceholder": "Click or press enter to select"
|
||||
},
|
||||
"datepickerRange": {
|
||||
"to": "To",
|
||||
"from": "From",
|
||||
"fromto": "{from} to {to}",
|
||||
"ranges": {
|
||||
"today": "Today",
|
||||
"thisWeek": "This Week",
|
||||
"restOfThisWeek": "The Rest of This Week",
|
||||
"nextWeek": "Next Week",
|
||||
"next7Days": "Next 7 Days",
|
||||
"lastWeek": "Last Week",
|
||||
"thisMonth": "This Month",
|
||||
"restOfThisMonth": "The Rest of This Month",
|
||||
"nextMonth": "Next Month",
|
||||
"next30Days": "Next 30 Days",
|
||||
"lastMonth": "Last Month",
|
||||
"thisYear": "This Year",
|
||||
"restOfThisYear": "The Rest of This Year"
|
||||
}
|
||||
},
|
||||
"datemathHelp": {
|
||||
"canuse": "You can use date math to filter for relative dates.",
|
||||
"learnhow": "Check out how it works",
|
||||
"title": "Date Math",
|
||||
"intro": "Date Math allows you to specifiy relative dates which are resolved on the fly by Vikunja when applying the filter.",
|
||||
"expression": "Each Date Math expression starts with an anchor date, which can either be {0}, or a date string ending with {1}. This anchor date can optionally be followed by one or more maths expressions.",
|
||||
"similar": "These expressions are similar to the ones provided by {0} and {1}.",
|
||||
"add1Day": "Add one day",
|
||||
"minus1Day": "Subtract one day",
|
||||
"roundDay": "Round down to the nearest day",
|
||||
"supportedUnits": "Supported time units are:",
|
||||
"someExamples": "Some examples of time expressions:",
|
||||
"units": {
|
||||
"seconds": "Seconds",
|
||||
"minutes": "Minutes",
|
||||
"hours": "Hours",
|
||||
"days": "Days",
|
||||
"weeks": "Weeks",
|
||||
"months": "Months",
|
||||
"years": "Years"
|
||||
},
|
||||
"examples": {
|
||||
"now": "Right now",
|
||||
"in24h": "In 24h",
|
||||
"today": "Today at 00:00",
|
||||
"beginningOfThisWeek": "The beginning of this week at 00:00",
|
||||
"endOfThisWeek": "The end of this week",
|
||||
"in30Days": "In 30 days",
|
||||
"datePlusMonth": "{0} plus one month at 00:00 of that day"
|
||||
}
|
||||
}
|
||||
},
|
||||
"task": {
|
||||
|
@ -541,12 +593,9 @@
|
|||
"titleCurrent": "Current Tasks",
|
||||
"titleDates": "Tasks from {from} until {to}",
|
||||
"noDates": "Show tasks without dates",
|
||||
"current": "Current tasks",
|
||||
"from": "Tasks from",
|
||||
"until": "until",
|
||||
"today": "Today",
|
||||
"nextWeek": "Next Week",
|
||||
"nextMonth": "Next Month",
|
||||
"overdue": "Show overdue tasks",
|
||||
"fromuntil": "Tasks from {from} until {until}",
|
||||
"select": "Select a date range",
|
||||
"noTasks": "Nothing to do — Have a nice day!"
|
||||
},
|
||||
"detail": {
|
||||
|
@ -570,22 +619,22 @@
|
|||
"text2": "This will also remove all attachments, reminders and relations associated with this task and cannot be undone!"
|
||||
},
|
||||
"actions": {
|
||||
"assign": "Assign to a user",
|
||||
"label": "Add labels",
|
||||
"assign": "Assign to User",
|
||||
"label": "Add Labels",
|
||||
"priority": "Set Priority",
|
||||
"dueDate": "Set Due Date",
|
||||
"startDate": "Set a Start Date",
|
||||
"endDate": "Set an End Date",
|
||||
"startDate": "Set Start Date",
|
||||
"endDate": "Set End Date",
|
||||
"reminders": "Set Reminders",
|
||||
"repeatAfter": "Set a repeating interval",
|
||||
"percentDone": "Set Percent Done",
|
||||
"attachments": "Add attachments",
|
||||
"relatedTasks": "Add task relations",
|
||||
"moveList": "Move task",
|
||||
"color": "Set task color",
|
||||
"delete": "Delete task",
|
||||
"favorite": "Save as favorite",
|
||||
"unfavorite": "Remove from favorites"
|
||||
"repeatAfter": "Set Repeating Interval",
|
||||
"percentDone": "Set Progress",
|
||||
"attachments": "Add Attachments",
|
||||
"relatedTasks": "Add Relation",
|
||||
"moveList": "Move",
|
||||
"color": "Set Color",
|
||||
"delete": "Delete",
|
||||
"favorite": "Add to Favorites",
|
||||
"unfavorite": "Remove from Favorites"
|
||||
}
|
||||
},
|
||||
"attributes": {
|
||||
|
@ -598,7 +647,7 @@
|
|||
"dueDate": "Due Date",
|
||||
"endDate": "End Date",
|
||||
"labels": "Labels",
|
||||
"percentDone": "% Done",
|
||||
"percentDone": "Progress",
|
||||
"priority": "Priority",
|
||||
"relatedTasks": "Related Tasks",
|
||||
"reminders": "Reminders",
|
||||
|
@ -785,17 +834,20 @@
|
|||
"general": "General",
|
||||
"allPages": "These shortcuts work on all pages.",
|
||||
"currentPageOnly": "These shortcuts work only on the current page.",
|
||||
"somePagesOnly": "These shortcuts work only on some pages.",
|
||||
"toggleMenu": "Toggle The Menu",
|
||||
"quickSearch": "Open the search/quick action bar",
|
||||
"then": "then",
|
||||
"task": {
|
||||
"title": "Task Page",
|
||||
"done": "Done",
|
||||
"assign": "Assign to a user",
|
||||
"done": "Mark task done / undone",
|
||||
"assign": "Assign this task to a user",
|
||||
"labels": "Add labels to this task",
|
||||
"dueDate": "Change the due date of this task",
|
||||
"attachment": "Add an attachment to this task",
|
||||
"related": "Modify related tasks of this task"
|
||||
"related": "Modify related tasks of this task",
|
||||
"color": "Change the color of this task",
|
||||
"move": "Move this task to another list"
|
||||
},
|
||||
"list": {
|
||||
"title": "List Views",
|
||||
|
@ -803,6 +855,14 @@
|
|||
"switchToGanttView": "Switch to gantt view",
|
||||
"switchToKanbanView": "Switch to kanban view",
|
||||
"switchToTableView": "Switch to table view"
|
||||
},
|
||||
"navigation": {
|
||||
"title": "Navigation",
|
||||
"overview": "Navigato to overview",
|
||||
"upcoming": "Navigato to upcoming taks",
|
||||
"namespaces": "Navigate to namepaces & lists",
|
||||
"labels": "Navigate to labels",
|
||||
"teams": "Navigate to teams"
|
||||
}
|
||||
},
|
||||
"update": {
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
"lastViewed": "Dernière consultation",
|
||||
"list": {
|
||||
"newText": "Tu peux créer une nouvelle liste pour tes nouvelles tâches :",
|
||||
"new": "New list",
|
||||
"new": "Nouvelle liste",
|
||||
"importText": "Ou importe tes listes et tâches d’autres services dans Vikunja :",
|
||||
"import": "Importer tes données dans Vikunja"
|
||||
}
|
||||
|
@ -31,11 +31,11 @@
|
|||
"username": "Nom d’utilisateur·rice",
|
||||
"usernameEmail": "Nom d’utilisateur·rice ou adresse courriel",
|
||||
"usernamePlaceholder": "p. ex. frederick",
|
||||
"email": "Email address",
|
||||
"email": "Adresse courriel",
|
||||
"emailPlaceholder": "p. ex. frederic{'@'}vikunja.io",
|
||||
"password": "Mot de passe",
|
||||
"passwordPlaceholder": "p. ex. •••••••••••",
|
||||
"forgotPassword": "Forgot your password?",
|
||||
"forgotPassword": "Mot de passe oublié ?",
|
||||
"resetPassword": "Réinitialiser ton mot de passe",
|
||||
"resetPasswordAction": "M’envoyer un lien de réinitialisation du mot de passe",
|
||||
"resetPasswordSuccess": "Vérifie ta boîte de réception ! Tu devrais avoir un courriel contenant les instructions sur la manière de réinitialiser ton mot de passe.",
|
||||
|
@ -44,20 +44,20 @@
|
|||
"totpTitle": "Code d’authentification à deux facteurs",
|
||||
"totpPlaceholder": "p. ex. 123456",
|
||||
"login": "Se connecter",
|
||||
"createAccount": "Create account",
|
||||
"createAccount": "Créer un compte",
|
||||
"loginWith": "Se connecter avec {provider}",
|
||||
"authenticating": "Authentification…",
|
||||
"openIdStateError": "L’état ne correspond pas, impossible de continuer !",
|
||||
"openIdGeneralError": "Une erreur s'est produite lors de l'authentification contre un tiers.",
|
||||
"logout": "Se déconnecter",
|
||||
"emailInvalid": "Please enter a valid email address.",
|
||||
"usernameRequired": "Please provide a username.",
|
||||
"passwordRequired": "Please provide a password.",
|
||||
"showPassword": "Show the password",
|
||||
"hidePassword": "Hide the password",
|
||||
"noAccountYet": "Don't have an account yet?",
|
||||
"alreadyHaveAnAccount": "Already have an account?",
|
||||
"remember": "Stay logged in"
|
||||
"emailInvalid": "Veuillez saisir une adresse courriel valide.",
|
||||
"usernameRequired": "Veuillez saisir un nom d'utilisateur.",
|
||||
"passwordRequired": "Veuillez fournir un mot de passe.",
|
||||
"showPassword": "Afficher le mot de passe",
|
||||
"hidePassword": "Masquer le mot de passe",
|
||||
"noAccountYet": "Vous n'avez pas encore de compte ?",
|
||||
"alreadyHaveAnAccount": "Vous avez déjà un compte ?",
|
||||
"remember": "Rester connecté(e)"
|
||||
},
|
||||
"settings": {
|
||||
"title": "Paramètres",
|
||||
|
@ -68,7 +68,7 @@
|
|||
"currentPasswordPlaceholder": "Ton mot de passe actuel",
|
||||
"passwordsDontMatch": "Le nouveau mot de passe et sa confirmation ne correspondent pas.",
|
||||
"passwordUpdateSuccess": "Mot de passe mis à jour.",
|
||||
"updateEmailTitle": "Update Your Email Address",
|
||||
"updateEmailTitle": "Mettre à jour votre adresse courriel",
|
||||
"updateEmailNew": "Nouvelle adresse courriel",
|
||||
"updateEmailSuccess": "Mise à jour de l’adresse électronique. Clique sur le lien dans le courriel qui t’a été envoyé pour le confirmer.",
|
||||
"general": {
|
||||
|
@ -86,7 +86,7 @@
|
|||
"weekStartMonday": "lundi",
|
||||
"language": "Langue",
|
||||
"defaultList": "Liste par défaut",
|
||||
"timezone": "Time Zone"
|
||||
"timezone": "Fuseau horaire"
|
||||
},
|
||||
"totp": {
|
||||
"title": "Authentification à deux facteurs",
|
||||
|
@ -111,7 +111,7 @@
|
|||
"title": "Avatar",
|
||||
"initials": "Initiales",
|
||||
"gravatar": "Gravatar",
|
||||
"marble": "Marble",
|
||||
"marble": "Bille",
|
||||
"upload": "Téléverser",
|
||||
"uploadAvatar": "Téléverser l’avatar",
|
||||
"statusUpdateSuccess": "Statut de l’avatar mis à jour.",
|
||||
|
@ -165,7 +165,7 @@
|
|||
"searchSelect": "Clique ou appuie sur la touche Entrée pour sélectionner cette liste",
|
||||
"shared": "Listes partagées",
|
||||
"create": {
|
||||
"header": "New list",
|
||||
"header": "Nouvelle liste",
|
||||
"titlePlaceholder": "Entre le nom de la liste…",
|
||||
"addTitleRequired": "Indique un nom.",
|
||||
"createdSuccess": "Liste créée.",
|
||||
|
@ -323,7 +323,7 @@
|
|||
"namespaces": "Espaces de noms",
|
||||
"search": "Écris pour rechercher un espace de noms…",
|
||||
"create": {
|
||||
"title": "New namespace",
|
||||
"title": "Nouvel espace de noms",
|
||||
"titleRequired": "Indique un nom.",
|
||||
"explanation": "Des collections de listes pour partager et organiser vos listes. En fait, chaque liste appartient à un espace de noms.",
|
||||
"tooltip": "Qu’est-ce qu’un espace de noms ?",
|
||||
|
@ -335,7 +335,7 @@
|
|||
"archiveText": "Tu ne pourras pas modifier cet espace de noms ou créer de nouvelles listes tant que tu ne l’auras pas désarchivé. Ceci archivera également toutes les listes de cet espace de noms.",
|
||||
"unarchiveText": "Tu pourras créer de nouvelles listes ou les modifier.",
|
||||
"success": "Espace de noms archivé.",
|
||||
"unarchiveSuccess": "The namespace was successfully un-archived.",
|
||||
"unarchiveSuccess": "Espace de noms archivé.",
|
||||
"description": "L’archivage d’un espace de noms signifie qu’on ne peut pas créer de nouvelles listes dans cet espace, ni le modifier."
|
||||
},
|
||||
"delete": {
|
||||
|
@ -383,16 +383,16 @@
|
|||
"includeNulls": "Inclure les tâches sans valeurs",
|
||||
"requireAll": "Exiger tous les filtres pour qu’une tâche s’affiche",
|
||||
"showDoneTasks": "Afficher les tâches terminées",
|
||||
"sortAlphabetically": "Sort Alphabetically",
|
||||
"sortAlphabetically": "Trier par ordre alphabétique",
|
||||
"enablePriority": "Activer le filtre par priorité",
|
||||
"enablePercentDone": "Par % d’achèvement",
|
||||
"enablePercentDone": "Activer le filtre par progression",
|
||||
"dueDateRange": "Plage de dates d’échéance",
|
||||
"startDateRange": "Plage de dates de début",
|
||||
"endDateRange": "Plage de dates de fin",
|
||||
"reminderRange": "Plage de dates de rappel"
|
||||
},
|
||||
"create": {
|
||||
"title": "New Saved Filter",
|
||||
"title": "Nouveau filtre enregistré",
|
||||
"description": "Un filtre enregistré est une liste virtuelle qui est calculée à partir d’un ensemble de filtres à chaque fois qu’on y accède. Une fois créé, il apparaît dans un espace de noms spécial.",
|
||||
"action": "Créer un nouveau filtre enregistré"
|
||||
},
|
||||
|
@ -485,7 +485,8 @@
|
|||
"showMenu": "Afficher le menu",
|
||||
"hideMenu": "Masquer le menu",
|
||||
"forExample": "Par exemple :",
|
||||
"welcomeBack": "Welcome Back!"
|
||||
"welcomeBack": "Heureux de vous revoir !",
|
||||
"custom": "Custom"
|
||||
},
|
||||
"input": {
|
||||
"resetColor": "Réinitialiser la couleur",
|
||||
|
@ -524,6 +525,57 @@
|
|||
"multiselect": {
|
||||
"createPlaceholder": "Créer un nouveau",
|
||||
"selectPlaceholder": "Clique ou appuie sur la touche Entrée pour sélectionner"
|
||||
},
|
||||
"datepickerRange": {
|
||||
"to": "To",
|
||||
"from": "From",
|
||||
"fromto": "{from} to {to}",
|
||||
"ranges": {
|
||||
"today": "Today",
|
||||
"thisWeek": "This Week",
|
||||
"restOfThisWeek": "The Rest of This Week",
|
||||
"nextWeek": "Next Week",
|
||||
"next7Days": "Next 7 Days",
|
||||
"lastWeek": "Last Week",
|
||||
"thisMonth": "This Month",
|
||||
"restOfThisMonth": "The Rest of This Month",
|
||||
"nextMonth": "Next Month",
|
||||
"next30Days": "Next 30 Days",
|
||||
"lastMonth": "Last Month",
|
||||
"thisYear": "This Year",
|
||||
"restOfThisYear": "The Rest of This Year"
|
||||
}
|
||||
},
|
||||
"datemathHelp": {
|
||||
"canuse": "You can use date math to filter for relative dates.",
|
||||
"learnhow": "Check out how it works",
|
||||
"title": "Date Math",
|
||||
"intro": "Date Math allows you to specifiy relative dates which are resolved on the fly by Vikunja when applying the filter.",
|
||||
"expression": "Each Date Math expression starts with an anchor date, which can either be {0}, or a date string ending with {1}. This anchor date can optionally be followed by one or more maths expressions.",
|
||||
"similar": "These expressions are similar to the ones provided by {0} and {1}.",
|
||||
"add1Day": "Add one day",
|
||||
"minus1Day": "Subtract one day",
|
||||
"roundDay": "Round down to the nearest day",
|
||||
"supportedUnits": "Supported time units are:",
|
||||
"someExamples": "Some examples of time expressions:",
|
||||
"units": {
|
||||
"seconds": "Seconds",
|
||||
"minutes": "Minutes",
|
||||
"hours": "Hours",
|
||||
"days": "Days",
|
||||
"weeks": "Weeks",
|
||||
"months": "Months",
|
||||
"years": "Years"
|
||||
},
|
||||
"examples": {
|
||||
"now": "Right now",
|
||||
"in24h": "In 24h",
|
||||
"today": "Today at 00:00",
|
||||
"beginningOfThisWeek": "The beginning of this week at 00:00",
|
||||
"endOfThisWeek": "The end of this week",
|
||||
"in30Days": "In 30 days",
|
||||
"datePlusMonth": "{0} plus one month at 00:00 of that day"
|
||||
}
|
||||
}
|
||||
},
|
||||
"task": {
|
||||
|
@ -541,12 +593,9 @@
|
|||
"titleCurrent": "Tâches actuelles",
|
||||
"titleDates": "Tâches du {from} au {to}",
|
||||
"noDates": "Afficher les tâches sans date",
|
||||
"current": "Tâches actuelles",
|
||||
"from": "Tâches du",
|
||||
"until": "au",
|
||||
"today": "Aujourd’hui",
|
||||
"nextWeek": "La semaine prochaine",
|
||||
"nextMonth": "Le mois prochain",
|
||||
"overdue": "Show overdue tasks",
|
||||
"fromuntil": "Tasks from {from} until {until}",
|
||||
"select": "Select a date range",
|
||||
"noTasks": "Rien à faire — Passe une bonne journée !"
|
||||
},
|
||||
"detail": {
|
||||
|
@ -554,7 +603,7 @@
|
|||
"chooseStartDate": "Clique ici pour fixer une date de début",
|
||||
"chooseEndDate": "Clique ici pour fixer une date de fin",
|
||||
"move": "Déplacer une tâche vers une autre liste",
|
||||
"done": "Mark task done!",
|
||||
"done": "Marquer la tâche comme terminée !",
|
||||
"undone": "Marquer comme inachevé",
|
||||
"created": "Créé {0} par {1}",
|
||||
"updated": "Mis à jour {0}",
|
||||
|
@ -570,21 +619,21 @@
|
|||
"text2": "Ceci supprimera également toutes les pièces jointes, les rappels et les relations associés à cette tâche et ne pourra pas être annulé !"
|
||||
},
|
||||
"actions": {
|
||||
"assign": "Attribuer à un utilisateur",
|
||||
"assign": "Assigner à l'utilisateur",
|
||||
"label": "Ajouter des étiquettes",
|
||||
"priority": "Définir la priorité",
|
||||
"dueDate": "Définir l’échéance",
|
||||
"startDate": "Définir une date de début",
|
||||
"endDate": "Fixer une date de fin",
|
||||
"startDate": "Définir la date de début",
|
||||
"endDate": "Définir la date de fin",
|
||||
"reminders": "Définir des rappels",
|
||||
"repeatAfter": "Définir un intervalle de répétition",
|
||||
"percentDone": "Définir le pourcentage d’achèvement",
|
||||
"percentDone": "Définir la progression",
|
||||
"attachments": "Ajouter des pièces jointes",
|
||||
"relatedTasks": "Ajouter des relations de tâches",
|
||||
"moveList": "Déplacer la tâche",
|
||||
"color": "Définir la couleur de la tâche",
|
||||
"delete": "Supprimer la tâche",
|
||||
"favorite": "Enregistrer comme favori",
|
||||
"relatedTasks": "Ajouter une relation",
|
||||
"moveList": "Déplacer",
|
||||
"color": "Définir la couleur",
|
||||
"delete": "Supprimer",
|
||||
"favorite": "Ajouter aux favoris",
|
||||
"unfavorite": "Retirer des favoris"
|
||||
}
|
||||
},
|
||||
|
@ -598,7 +647,7 @@
|
|||
"dueDate": "Date d’échéance",
|
||||
"endDate": "Date de fin",
|
||||
"labels": "Étiquettes",
|
||||
"percentDone": "% terminé",
|
||||
"percentDone": "Progression",
|
||||
"priority": "Priorité",
|
||||
"relatedTasks": "Tâches connexes",
|
||||
"reminders": "Rappels",
|
||||
|
@ -785,17 +834,20 @@
|
|||
"general": "Général",
|
||||
"allPages": "Fonctionne sur toutes les pages.",
|
||||
"currentPageOnly": "Fonctionnent uniquement sur la page en cours.",
|
||||
"somePagesOnly": "Ces raccourcis fonctionnent seulement sur certaines pages.",
|
||||
"toggleMenu": "Basculer le menu",
|
||||
"quickSearch": "Ouvrir la barre de recherche/action rapide",
|
||||
"then": "puis",
|
||||
"task": {
|
||||
"title": "Page de tâche",
|
||||
"done": "Done",
|
||||
"assign": "Attribuer à un utilisateur",
|
||||
"done": "Mark task done / undone",
|
||||
"assign": "Assigner cette tâche à un utilisateur",
|
||||
"labels": "Ajouter des étiquettes à cette tâche",
|
||||
"dueDate": "Modifier la date d’échéance de cette tâche",
|
||||
"attachment": "Ajouter une pièce jointe à cette tâche",
|
||||
"related": "Modifier les tâches connexes de cette tâche"
|
||||
"related": "Modifier les tâches connexes de cette tâche",
|
||||
"color": "Changer la couleur de cette tâche",
|
||||
"move": "Déplacer cette tâche dans une autre liste"
|
||||
},
|
||||
"list": {
|
||||
"title": "Vues en liste",
|
||||
|
@ -803,6 +855,14 @@
|
|||
"switchToGanttView": "Passer en vue Gantt",
|
||||
"switchToKanbanView": "Passer en vue kanban",
|
||||
"switchToTableView": "Passer en vue tableau"
|
||||
},
|
||||
"navigation": {
|
||||
"title": "Navigation",
|
||||
"overview": "Accéder à la vue d'ensemble",
|
||||
"upcoming": "Accéder aux prochaines tâches",
|
||||
"namespaces": "Accéder aux espaces de noms et listes",
|
||||
"labels": "Accéder aux étiquettes",
|
||||
"teams": "Accéder aux équipes"
|
||||
}
|
||||
},
|
||||
"update": {
|
||||
|
@ -823,7 +883,7 @@
|
|||
"url": "URL Vikunja",
|
||||
"urlPlaceholder": "Par exemple : https://localhost:3456",
|
||||
"change": "changer",
|
||||
"use": "Using Vikunja installation at {0}",
|
||||
"use": "Utiliser l’installation de Vikunja à {0}",
|
||||
"error": "Impossible de trouver ou d'utiliser l'installation de Vikunja sur « {domain} ». Veuillez essayer une autre URL.",
|
||||
"success": "Utilisation de l’installation Vikunja à « {domain} ».",
|
||||
"urlRequired": "Une URL est requise."
|
||||
|
@ -908,7 +968,7 @@
|
|||
"4015": "Le commentaire de la tâche n’existe pas.",
|
||||
"4016": "Champ de tâche invalide.",
|
||||
"4017": "Comparateur de filtre de tâche invalide.",
|
||||
"4018": "Invalid task filter concatenator.",
|
||||
"4018": "Concaténateur de filtre de tâche invalide.",
|
||||
"4019": "Valeur de filtre de tâche invalide.",
|
||||
"5001": "L’espace de noms n’existe pas.",
|
||||
"5003": "Tu n’as pas accès à l’espace de noms indiqué.",
|
||||
|
|
|
@ -103,9 +103,9 @@
|
|||
"disableSuccess": "L'autenticazione a due fattori è stata disattivata."
|
||||
},
|
||||
"caldav": {
|
||||
"title": "CalDav",
|
||||
"howTo": "Puoi connettere Vikunja ai client caldav per visualizzare e gestire tutte le attività da diversi client. Inserisci questo URL nel tuo client:",
|
||||
"more": "Ulteriori informazioni su caldav in Vikunja"
|
||||
"title": "CalDAV",
|
||||
"howTo": "Puoi connettere Vikunja ai client CalDAV per visualizzare e gestire tutte le attività da diversi client. Inserisci questo URL nel tuo client:",
|
||||
"more": "Ulteriori informazioni su CalDAV in Vikunja"
|
||||
},
|
||||
"avatar": {
|
||||
"title": "Avatar",
|
||||
|
@ -335,7 +335,7 @@
|
|||
"archiveText": "Non sarà possibile modificare questo namespace o creare nuove liste fino a quando non verrà disarchiviato. Questo archivierà anche tutte le liste in questo namespace.",
|
||||
"unarchiveText": "Potrai creare nuove liste o modificarle.",
|
||||
"success": "Namespace creato.",
|
||||
"unarchiveSuccess": "The namespace was successfully un-archived.",
|
||||
"unarchiveSuccess": "Namespace estratto dall'archivio.",
|
||||
"description": "Se un namespace è archiviato, non è possibile creare nuove liste o modificarlo."
|
||||
},
|
||||
"delete": {
|
||||
|
@ -385,7 +385,7 @@
|
|||
"showDoneTasks": "Mostra Attività Fatte",
|
||||
"sortAlphabetically": "Ordine alfabetico",
|
||||
"enablePriority": "Abilita Filtro Per Priorità",
|
||||
"enablePercentDone": "Abilitare Filtro Per Percentuale Fatta",
|
||||
"enablePercentDone": "Abilita Filtro Per Progresso",
|
||||
"dueDateRange": "Intervallo Data Di Scadenza",
|
||||
"startDateRange": "Intervallo Data Iniziale",
|
||||
"endDateRange": "Intervallo Data Finale",
|
||||
|
@ -485,7 +485,8 @@
|
|||
"showMenu": "Mostra il menu",
|
||||
"hideMenu": "Nascondi il menù",
|
||||
"forExample": "Ad esempio:",
|
||||
"welcomeBack": "Bentornato!"
|
||||
"welcomeBack": "Bentornato!",
|
||||
"custom": "Custom"
|
||||
},
|
||||
"input": {
|
||||
"resetColor": "Ripristina Colore",
|
||||
|
@ -524,6 +525,57 @@
|
|||
"multiselect": {
|
||||
"createPlaceholder": "Crea nuovo",
|
||||
"selectPlaceholder": "Clicca o premere invio per selezionare"
|
||||
},
|
||||
"datepickerRange": {
|
||||
"to": "To",
|
||||
"from": "From",
|
||||
"fromto": "{from} to {to}",
|
||||
"ranges": {
|
||||
"today": "Today",
|
||||
"thisWeek": "This Week",
|
||||
"restOfThisWeek": "The Rest of This Week",
|
||||
"nextWeek": "Next Week",
|
||||
"next7Days": "Next 7 Days",
|
||||
"lastWeek": "Last Week",
|
||||
"thisMonth": "This Month",
|
||||
"restOfThisMonth": "The Rest of This Month",
|
||||
"nextMonth": "Next Month",
|
||||
"next30Days": "Next 30 Days",
|
||||
"lastMonth": "Last Month",
|
||||
"thisYear": "This Year",
|
||||
"restOfThisYear": "The Rest of This Year"
|
||||
}
|
||||
},
|
||||
"datemathHelp": {
|
||||
"canuse": "You can use date math to filter for relative dates.",
|
||||
"learnhow": "Check out how it works",
|
||||
"title": "Date Math",
|
||||
"intro": "Date Math allows you to specifiy relative dates which are resolved on the fly by Vikunja when applying the filter.",
|
||||
"expression": "Each Date Math expression starts with an anchor date, which can either be {0}, or a date string ending with {1}. This anchor date can optionally be followed by one or more maths expressions.",
|
||||
"similar": "These expressions are similar to the ones provided by {0} and {1}.",
|
||||
"add1Day": "Add one day",
|
||||
"minus1Day": "Subtract one day",
|
||||
"roundDay": "Round down to the nearest day",
|
||||
"supportedUnits": "Supported time units are:",
|
||||
"someExamples": "Some examples of time expressions:",
|
||||
"units": {
|
||||
"seconds": "Seconds",
|
||||
"minutes": "Minutes",
|
||||
"hours": "Hours",
|
||||
"days": "Days",
|
||||
"weeks": "Weeks",
|
||||
"months": "Months",
|
||||
"years": "Years"
|
||||
},
|
||||
"examples": {
|
||||
"now": "Right now",
|
||||
"in24h": "In 24h",
|
||||
"today": "Today at 00:00",
|
||||
"beginningOfThisWeek": "The beginning of this week at 00:00",
|
||||
"endOfThisWeek": "The end of this week",
|
||||
"in30Days": "In 30 days",
|
||||
"datePlusMonth": "{0} plus one month at 00:00 of that day"
|
||||
}
|
||||
}
|
||||
},
|
||||
"task": {
|
||||
|
@ -541,12 +593,9 @@
|
|||
"titleCurrent": "Attività Attuali",
|
||||
"titleDates": "Attività dal {from} al {to}",
|
||||
"noDates": "Mostra attività senza date",
|
||||
"current": "Attività attuali",
|
||||
"from": "Attività dal",
|
||||
"until": "fino al",
|
||||
"today": "Oggi",
|
||||
"nextWeek": "Settimana Prossima",
|
||||
"nextMonth": "Prossimo Mese",
|
||||
"overdue": "Show overdue tasks",
|
||||
"fromuntil": "Tasks from {from} until {until}",
|
||||
"select": "Select a date range",
|
||||
"noTasks": "Nessuna attività — Buona giornata!"
|
||||
},
|
||||
"detail": {
|
||||
|
@ -570,22 +619,22 @@
|
|||
"text2": "Questo rimuoverà anche tutti gli allegati, i promemoria e le relazioni associati a questa attività e non può essere ripristinato!"
|
||||
},
|
||||
"actions": {
|
||||
"assign": "Assegna ad un utente",
|
||||
"label": "Aggiungi etichette",
|
||||
"assign": "Assegna all'Utente",
|
||||
"label": "Aggiungi Etichette",
|
||||
"priority": "Imposta Priorità",
|
||||
"dueDate": "Imposta data di scadenza",
|
||||
"startDate": "Imposta una data di inizio",
|
||||
"endDate": "Imposta una data di fine",
|
||||
"startDate": "Imposta Data Inizio",
|
||||
"endDate": "Imposta Data Fine",
|
||||
"reminders": "Imposta promemoria",
|
||||
"repeatAfter": "Imposta ricorrenza",
|
||||
"percentDone": "Imposta Percentuale Completata",
|
||||
"attachments": "Aggiungi allegati",
|
||||
"relatedTasks": "Aggiungi attività collegate",
|
||||
"moveList": "Sposta attività",
|
||||
"color": "Imposta colore attività",
|
||||
"delete": "Elimina attività",
|
||||
"favorite": "Salva come preferito",
|
||||
"unfavorite": "Rimuovi dai preferiti"
|
||||
"repeatAfter": "Imposta Intervallo Ripetizione",
|
||||
"percentDone": "Imposta Progresso",
|
||||
"attachments": "Aggiungi Allegati",
|
||||
"relatedTasks": "Aggiungi Relazione",
|
||||
"moveList": "Sposta",
|
||||
"color": "Imposta Colore",
|
||||
"delete": "Elimina",
|
||||
"favorite": "Aggiungi ai Preferiti",
|
||||
"unfavorite": "Rimuovi dai Preferiti"
|
||||
}
|
||||
},
|
||||
"attributes": {
|
||||
|
@ -598,7 +647,7 @@
|
|||
"dueDate": "Data di scadenza",
|
||||
"endDate": "Data di fine",
|
||||
"labels": "Etichette",
|
||||
"percentDone": "% Completata",
|
||||
"percentDone": "Progresso",
|
||||
"priority": "Priorità",
|
||||
"relatedTasks": "Attività Collegate",
|
||||
"reminders": "Promemoria",
|
||||
|
@ -785,17 +834,20 @@
|
|||
"general": "Generali",
|
||||
"allPages": "Queste scorciatoie funzionano in tutte le pagine.",
|
||||
"currentPageOnly": "Queste scorciatoie funzionano solo nella pagina attuale.",
|
||||
"somePagesOnly": "These shortcuts work only on some pages.",
|
||||
"toggleMenu": "Attiva/Disattiva Menu",
|
||||
"quickSearch": "Apri la barra di ricerca/azione rapida",
|
||||
"then": "e dopo",
|
||||
"task": {
|
||||
"title": "Pagina Attività",
|
||||
"done": "Fatto",
|
||||
"assign": "Assegna a un utente",
|
||||
"done": "Mark task done / undone",
|
||||
"assign": "Assign this task to a user",
|
||||
"labels": "Aggiungi etichette a questa attività",
|
||||
"dueDate": "Modifica la data di scadenza di questa attività",
|
||||
"attachment": "Aggiungi un allegato a questa attività",
|
||||
"related": "Modifica le attività collegate a questa"
|
||||
"related": "Modifica le attività collegate a questa",
|
||||
"color": "Change the color of this task",
|
||||
"move": "Move this task to another list"
|
||||
},
|
||||
"list": {
|
||||
"title": "Viste Liste",
|
||||
|
@ -803,6 +855,14 @@
|
|||
"switchToGanttView": "Passa alla vista Gantt",
|
||||
"switchToKanbanView": "Passa alla vista Kanban",
|
||||
"switchToTableView": "Passa alla vista Tabella"
|
||||
},
|
||||
"navigation": {
|
||||
"title": "Navigation",
|
||||
"overview": "Navigato to overview",
|
||||
"upcoming": "Navigato to upcoming taks",
|
||||
"namespaces": "Navigate to namepaces & lists",
|
||||
"labels": "Navigate to labels",
|
||||
"teams": "Navigate to teams"
|
||||
}
|
||||
},
|
||||
"update": {
|
||||
|
|
|
@ -67,7 +67,7 @@
|
|||
"currentPassword": "Huidig wachtwoord",
|
||||
"currentPasswordPlaceholder": "Je huidige wachtwoord",
|
||||
"passwordsDontMatch": "Het nieuwe wachtwoord en de bevestiging ervan komen niet overeen.",
|
||||
"passwordUpdateSuccess": "Het wachtwoord is succesvol gewijzigd.",
|
||||
"passwordUpdateSuccess": "Het wachtwoord is succesvol bijgewerkt.",
|
||||
"updateEmailTitle": "Update Your Email Address",
|
||||
"updateEmailNew": "Nieuw e-mailadres",
|
||||
"updateEmailSuccess": "Je e-mailadres is succesvol bijgewerkt. We hebben je een link gestuurd om het te bevestigen.",
|
||||
|
@ -91,15 +91,15 @@
|
|||
"totp": {
|
||||
"title": "Twee-factor-authenticatie",
|
||||
"enroll": "Activeren",
|
||||
"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.",
|
||||
"finishSetupPart1": "Gebruik dit geheim in je TOTP app (Google Authenticator of vergelijkbaar), om de installatie te voltooien:",
|
||||
"finishSetupPart2": "Daarna voert u hieronder een code van je app in.",
|
||||
"scanQR": "Als alternatief kan je ook deze QR code scannen:",
|
||||
"passcode": "Je toegangscode",
|
||||
"passcodePlaceholder": "A code generated by your totp application",
|
||||
"passcodePlaceholder": "Een code gegenereerd door uw TOTP app",
|
||||
"setupSuccess": "Je hebt met succes tweestapsverificatie ingesteld!",
|
||||
"enterPassword": "Voer alsjeblieft je wachtwoord in",
|
||||
"disable": "Tweestapsverificatie uitschakelen",
|
||||
"confirmSuccess": "You've successfully confirmed your totp setup and can use it from now on!",
|
||||
"confirmSuccess": "Je hebt met succes je TOTP setup bevestigd en kan het van nu af aan gebruiken!",
|
||||
"disableSuccess": "Authenticatie in twee stappen is succesvol uitgeschakeld."
|
||||
},
|
||||
"caldav": {
|
||||
|
@ -118,7 +118,7 @@
|
|||
"setSuccess": "De avatar is succesvol ingesteld!"
|
||||
},
|
||||
"quickAddMagic": {
|
||||
"title": "Quick Add Magic Mode",
|
||||
"title": "Magische Modus Snel Toevoegen",
|
||||
"disabled": "Uitgeschakeld",
|
||||
"todoist": "Todoist",
|
||||
"vikunja": "Vikunja"
|
||||
|
@ -136,7 +136,7 @@
|
|||
"deletion": {
|
||||
"title": "Verwijder je Vikunja account",
|
||||
"text1": "Het verwijderen van je account is permanent en kan niet ongedaan worden gemaakt. We zullen al je namespaces, lijsten, taken en alles wat ermee verbonden is verwijderen.",
|
||||
"text2": "To proceed, please enter your password. You will receive an email with further instructions.",
|
||||
"text2": "Graag je wachtwoord invullen om verder te gaan. Je zult een e-mail ontvangen met verdere instructies.",
|
||||
"confirm": "Verwijder mijn account",
|
||||
"requestSuccess": "Het verzoek was succesvol. Je ontvangt een e-mail met verdere instructies.",
|
||||
"passwordRequired": "Voer alsjeblieft je wachtwoord in.",
|
||||
|
@ -335,6 +335,7 @@
|
|||
"archiveText": "You won't be able to edit this namespace or create new lists until you un-archive it. This will also archive all lists in this namespace.",
|
||||
"unarchiveText": "Je kan nieuwe lijsten maken of deze bewerken.",
|
||||
"success": "The namespace was successfully archived.",
|
||||
"unarchiveSuccess": "The namespace was successfully un-archived.",
|
||||
"description": "If a namespace is archived, you cannot create new lists or edit it."
|
||||
},
|
||||
"delete": {
|
||||
|
@ -384,7 +385,7 @@
|
|||
"showDoneTasks": "Show Done Tasks",
|
||||
"sortAlphabetically": "Alfabetisch sorteren",
|
||||
"enablePriority": "Enable Filter By Priority",
|
||||
"enablePercentDone": "Enable Filter By Percent Done",
|
||||
"enablePercentDone": "Enable Filter By Progress",
|
||||
"dueDateRange": "Due Date Range",
|
||||
"startDateRange": "Start Date Range",
|
||||
"endDateRange": "End Date Range",
|
||||
|
@ -484,7 +485,8 @@
|
|||
"showMenu": "Menu weergeven",
|
||||
"hideMenu": "Menu verbergen",
|
||||
"forExample": "Bijvoorbeeld:",
|
||||
"welcomeBack": "Welkom terug!"
|
||||
"welcomeBack": "Welkom terug!",
|
||||
"custom": "Custom"
|
||||
},
|
||||
"input": {
|
||||
"resetColor": "Kleur resetten",
|
||||
|
@ -499,7 +501,7 @@
|
|||
},
|
||||
"editor": {
|
||||
"edit": "Bewerken",
|
||||
"done": "Gereed",
|
||||
"done": "Voltooid",
|
||||
"heading1": "Kop 1",
|
||||
"heading2": "Kop 2",
|
||||
"heading3": "Kop 3",
|
||||
|
@ -523,6 +525,57 @@
|
|||
"multiselect": {
|
||||
"createPlaceholder": "Nieuwe aanmaken",
|
||||
"selectPlaceholder": "Klik of druk op enter om te selecteren"
|
||||
},
|
||||
"datepickerRange": {
|
||||
"to": "To",
|
||||
"from": "From",
|
||||
"fromto": "{from} to {to}",
|
||||
"ranges": {
|
||||
"today": "Today",
|
||||
"thisWeek": "This Week",
|
||||
"restOfThisWeek": "The Rest of This Week",
|
||||
"nextWeek": "Next Week",
|
||||
"next7Days": "Next 7 Days",
|
||||
"lastWeek": "Last Week",
|
||||
"thisMonth": "This Month",
|
||||
"restOfThisMonth": "The Rest of This Month",
|
||||
"nextMonth": "Next Month",
|
||||
"next30Days": "Next 30 Days",
|
||||
"lastMonth": "Last Month",
|
||||
"thisYear": "This Year",
|
||||
"restOfThisYear": "The Rest of This Year"
|
||||
}
|
||||
},
|
||||
"datemathHelp": {
|
||||
"canuse": "You can use date math to filter for relative dates.",
|
||||
"learnhow": "Check out how it works",
|
||||
"title": "Date Math",
|
||||
"intro": "Date Math allows you to specifiy relative dates which are resolved on the fly by Vikunja when applying the filter.",
|
||||
"expression": "Each Date Math expression starts with an anchor date, which can either be {0}, or a date string ending with {1}. This anchor date can optionally be followed by one or more maths expressions.",
|
||||
"similar": "These expressions are similar to the ones provided by {0} and {1}.",
|
||||
"add1Day": "Add one day",
|
||||
"minus1Day": "Subtract one day",
|
||||
"roundDay": "Round down to the nearest day",
|
||||
"supportedUnits": "Supported time units are:",
|
||||
"someExamples": "Some examples of time expressions:",
|
||||
"units": {
|
||||
"seconds": "Seconds",
|
||||
"minutes": "Minutes",
|
||||
"hours": "Hours",
|
||||
"days": "Days",
|
||||
"weeks": "Weeks",
|
||||
"months": "Months",
|
||||
"years": "Years"
|
||||
},
|
||||
"examples": {
|
||||
"now": "Right now",
|
||||
"in24h": "In 24h",
|
||||
"today": "Today at 00:00",
|
||||
"beginningOfThisWeek": "The beginning of this week at 00:00",
|
||||
"endOfThisWeek": "The end of this week",
|
||||
"in30Days": "In 30 days",
|
||||
"datePlusMonth": "{0} plus one month at 00:00 of that day"
|
||||
}
|
||||
}
|
||||
},
|
||||
"task": {
|
||||
|
@ -540,12 +593,9 @@
|
|||
"titleCurrent": "Huidige taken",
|
||||
"titleDates": "Tasks from {from} until {to}",
|
||||
"noDates": "Taken zonder datums tonen",
|
||||
"current": "Huidige taken",
|
||||
"from": "Taken van",
|
||||
"until": "tot",
|
||||
"today": "Vandaag",
|
||||
"nextWeek": "Volgende week",
|
||||
"nextMonth": "Volgende maand",
|
||||
"overdue": "Show overdue tasks",
|
||||
"fromuntil": "Tasks from {from} until {until}",
|
||||
"select": "Select a date range",
|
||||
"noTasks": "Nothing to do — Have a nice day!"
|
||||
},
|
||||
"detail": {
|
||||
|
@ -554,7 +604,7 @@
|
|||
"chooseEndDate": "Klik hier om een einddatum in te stellen",
|
||||
"move": "Verplaats taak naar een andere lijst",
|
||||
"done": "Mark task done!",
|
||||
"undone": "Markeer als niet gereed",
|
||||
"undone": "Markeer als niet voltooid",
|
||||
"created": "Created {0} by {1}",
|
||||
"updated": "Updated {0}",
|
||||
"doneAt": "Done {0}",
|
||||
|
@ -569,22 +619,22 @@
|
|||
"text2": "This will also remove all attachments, reminders and relations associated with this task and cannot be undone!"
|
||||
},
|
||||
"actions": {
|
||||
"assign": "Toewijzen aan een gebruiker",
|
||||
"label": "Labels toevoegen",
|
||||
"assign": "Assign to User",
|
||||
"label": "Add Labels",
|
||||
"priority": "Prioriteit instellen",
|
||||
"dueDate": "Vervaldatum instellen",
|
||||
"startDate": "Startdatum instellen",
|
||||
"endDate": "Einddatum instellen",
|
||||
"startDate": "Set Start Date",
|
||||
"endDate": "Set End Date",
|
||||
"reminders": "Herinneringen instellen",
|
||||
"repeatAfter": "Herhalingsinterval instellen",
|
||||
"percentDone": "Percentage gereed instellen",
|
||||
"attachments": "Bijlage toevoegen",
|
||||
"relatedTasks": "Taakrelaties toevoegen",
|
||||
"moveList": "Taak verplaatsen",
|
||||
"color": "Taakkleur instellen",
|
||||
"delete": "Taak verwijderen",
|
||||
"favorite": "Opslaan als favoriet",
|
||||
"unfavorite": "Verwijderen uit favorieten"
|
||||
"repeatAfter": "Set Repeating Interval",
|
||||
"percentDone": "Set Progress",
|
||||
"attachments": "Add Attachments",
|
||||
"relatedTasks": "Add Relation",
|
||||
"moveList": "Move",
|
||||
"color": "Set Color",
|
||||
"delete": "Verwijder",
|
||||
"favorite": "Toevoegen aan favorieten",
|
||||
"unfavorite": "Verwijder uit favorieten"
|
||||
}
|
||||
},
|
||||
"attributes": {
|
||||
|
@ -593,11 +643,11 @@
|
|||
"created": "Aangemaakt op",
|
||||
"createdBy": "Aangemaakt door",
|
||||
"description": "Beschrijving",
|
||||
"done": "Gereed",
|
||||
"done": "Voltooid",
|
||||
"dueDate": "Due Date",
|
||||
"endDate": "End Date",
|
||||
"labels": "Labels",
|
||||
"percentDone": "% Gereed",
|
||||
"percentDone": "Progress",
|
||||
"priority": "Prioriteit",
|
||||
"relatedTasks": "Verwante Taken",
|
||||
"reminders": "Herinneringen",
|
||||
|
@ -784,17 +834,20 @@
|
|||
"general": "General",
|
||||
"allPages": "These shortcuts work on all pages.",
|
||||
"currentPageOnly": "These shortcuts work only on the current page.",
|
||||
"somePagesOnly": "These shortcuts work only on some pages.",
|
||||
"toggleMenu": "Toggle The Menu",
|
||||
"quickSearch": "Open the search/quick action bar",
|
||||
"then": "then",
|
||||
"task": {
|
||||
"title": "Task Page",
|
||||
"done": "Done",
|
||||
"assign": "Assign to a user",
|
||||
"done": "Mark task done / undone",
|
||||
"assign": "Assign this task to a user",
|
||||
"labels": "Add labels to this task",
|
||||
"dueDate": "Change the due date of this task",
|
||||
"attachment": "Add an attachment to this task",
|
||||
"related": "Modify related tasks of this task"
|
||||
"related": "Modify related tasks of this task",
|
||||
"color": "Change the color of this task",
|
||||
"move": "Move this task to another list"
|
||||
},
|
||||
"list": {
|
||||
"title": "List Views",
|
||||
|
@ -802,6 +855,14 @@
|
|||
"switchToGanttView": "Switch to gantt view",
|
||||
"switchToKanbanView": "Switch to kanban view",
|
||||
"switchToTableView": "Switch to table view"
|
||||
},
|
||||
"navigation": {
|
||||
"title": "Navigation",
|
||||
"overview": "Navigato to overview",
|
||||
"upcoming": "Navigato to upcoming taks",
|
||||
"namespaces": "Navigate to namepaces & lists",
|
||||
"labels": "Navigate to labels",
|
||||
"teams": "Navigate to teams"
|
||||
}
|
||||
},
|
||||
"update": {
|
||||
|
|
|
@ -105,7 +105,7 @@
|
|||
"caldav": {
|
||||
"title": "Caldav",
|
||||
"howTo": "Możesz połączyć Vikunja z klientami caldav, aby przeglądać i zarządzać wszystkimi zadaniami z różnych klientów. Wprowadź ten adres URL do swojego klienta:",
|
||||
"more": "Więcej informacji o caldav w Vikunji"
|
||||
"more": "Więcej informacji o CalDAV w Vikunji"
|
||||
},
|
||||
"avatar": {
|
||||
"title": "Awatar",
|
||||
|
@ -335,7 +335,7 @@
|
|||
"archiveText": "Nie będziesz mógł tworzyć nowych list ani edytować tej sekcji, dopóki nie cofniesz archiwizacji. Ta operacja zarchiwizuje również wszystkie listy należące do tej sekcji.",
|
||||
"unarchiveText": "Będziesz mógł tworzyć nowe listy i je edytować.",
|
||||
"success": "Sekcja została pomyślnie zarchiwizowana.",
|
||||
"unarchiveSuccess": "The namespace was successfully un-archived.",
|
||||
"unarchiveSuccess": "Archiwizacja sekcji została pomyślnie cofnięta.",
|
||||
"description": "Jeśli sekcja jest zarchiwizowana, nie można edytować ani tworzyć nowych list."
|
||||
},
|
||||
"delete": {
|
||||
|
@ -357,7 +357,7 @@
|
|||
"description": "Opis",
|
||||
"descriptionPlaceholder": "Tu wpisz opis sekcji…",
|
||||
"color": "Kolor",
|
||||
"archived": "Zarchiwizowany",
|
||||
"archived": "Archiwizacja",
|
||||
"isArchived": "Ta sekcja jest zarchiwizowana"
|
||||
},
|
||||
"pseudo": {
|
||||
|
@ -385,7 +385,7 @@
|
|||
"showDoneTasks": "Pokaż ukończone zadania",
|
||||
"sortAlphabetically": "Sortuj alfabetycznie",
|
||||
"enablePriority": "Włącz filtrowanie według priorytetu",
|
||||
"enablePercentDone": "Włącz filtrowanie według procentu ukończenia",
|
||||
"enablePercentDone": "Włącz filtrowanie według postępu",
|
||||
"dueDateRange": "Zakres daty terminu",
|
||||
"startDateRange": "Zakres daty rozpoczęcia",
|
||||
"endDateRange": "Zakres daty zakończenia",
|
||||
|
@ -485,7 +485,8 @@
|
|||
"showMenu": "Pokaż menu",
|
||||
"hideMenu": "Ukryj menu",
|
||||
"forExample": "Na przykład:",
|
||||
"welcomeBack": "Witaj ponownie!"
|
||||
"welcomeBack": "Witaj ponownie!",
|
||||
"custom": "Custom"
|
||||
},
|
||||
"input": {
|
||||
"resetColor": "Resetuj kolor",
|
||||
|
@ -524,6 +525,57 @@
|
|||
"multiselect": {
|
||||
"createPlaceholder": "Utwórz nowy",
|
||||
"selectPlaceholder": "Kliknij lub naciśnij Enter, aby wybrać"
|
||||
},
|
||||
"datepickerRange": {
|
||||
"to": "To",
|
||||
"from": "From",
|
||||
"fromto": "{from} to {to}",
|
||||
"ranges": {
|
||||
"today": "Today",
|
||||
"thisWeek": "This Week",
|
||||
"restOfThisWeek": "The Rest of This Week",
|
||||
"nextWeek": "Next Week",
|
||||
"next7Days": "Next 7 Days",
|
||||
"lastWeek": "Last Week",
|
||||
"thisMonth": "This Month",
|
||||
"restOfThisMonth": "The Rest of This Month",
|
||||
"nextMonth": "Next Month",
|
||||
"next30Days": "Next 30 Days",
|
||||
"lastMonth": "Last Month",
|
||||
"thisYear": "This Year",
|
||||
"restOfThisYear": "The Rest of This Year"
|
||||
}
|
||||
},
|
||||
"datemathHelp": {
|
||||
"canuse": "You can use date math to filter for relative dates.",
|
||||
"learnhow": "Check out how it works",
|
||||
"title": "Date Math",
|
||||
"intro": "Date Math allows you to specifiy relative dates which are resolved on the fly by Vikunja when applying the filter.",
|
||||
"expression": "Each Date Math expression starts with an anchor date, which can either be {0}, or a date string ending with {1}. This anchor date can optionally be followed by one or more maths expressions.",
|
||||
"similar": "These expressions are similar to the ones provided by {0} and {1}.",
|
||||
"add1Day": "Add one day",
|
||||
"minus1Day": "Subtract one day",
|
||||
"roundDay": "Round down to the nearest day",
|
||||
"supportedUnits": "Supported time units are:",
|
||||
"someExamples": "Some examples of time expressions:",
|
||||
"units": {
|
||||
"seconds": "Seconds",
|
||||
"minutes": "Minutes",
|
||||
"hours": "Hours",
|
||||
"days": "Days",
|
||||
"weeks": "Weeks",
|
||||
"months": "Months",
|
||||
"years": "Years"
|
||||
},
|
||||
"examples": {
|
||||
"now": "Right now",
|
||||
"in24h": "In 24h",
|
||||
"today": "Today at 00:00",
|
||||
"beginningOfThisWeek": "The beginning of this week at 00:00",
|
||||
"endOfThisWeek": "The end of this week",
|
||||
"in30Days": "In 30 days",
|
||||
"datePlusMonth": "{0} plus one month at 00:00 of that day"
|
||||
}
|
||||
}
|
||||
},
|
||||
"task": {
|
||||
|
@ -541,12 +593,9 @@
|
|||
"titleCurrent": "Bieżące zadania",
|
||||
"titleDates": "Zadania od {from} do {to}",
|
||||
"noDates": "Pokaż zadania bez dat",
|
||||
"current": "Bieżące zadania",
|
||||
"from": "Zadania od",
|
||||
"until": "do",
|
||||
"today": "Dziś",
|
||||
"nextWeek": "Następny tydzień",
|
||||
"nextMonth": "Następny miesiąc",
|
||||
"overdue": "Show overdue tasks",
|
||||
"fromuntil": "Tasks from {from} until {until}",
|
||||
"select": "Select a date range",
|
||||
"noTasks": "Brak zadań do wykonania – miłego dnia!"
|
||||
},
|
||||
"detail": {
|
||||
|
@ -578,13 +627,13 @@
|
|||
"endDate": "Ustaw datę zakończenia",
|
||||
"reminders": "Ustaw przypomnienia",
|
||||
"repeatAfter": "Ustaw interwał powtarzania",
|
||||
"percentDone": "Ustaw procent ukończenia",
|
||||
"percentDone": "Ustaw postęp",
|
||||
"attachments": "Dodaj załączniki",
|
||||
"relatedTasks": "Dodaj powiązane zadania",
|
||||
"moveList": "Przenieś zadanie",
|
||||
"color": "Ustaw kolor zadania",
|
||||
"delete": "Usuń zadanie",
|
||||
"favorite": "Zapisz jako ulubione",
|
||||
"relatedTasks": "Dodaj powiązanie",
|
||||
"moveList": "Przenieś",
|
||||
"color": "Ustaw kolor",
|
||||
"delete": "Usuń",
|
||||
"favorite": "Dodaj do ulubionych",
|
||||
"unfavorite": "Usuń z ulubionych"
|
||||
}
|
||||
},
|
||||
|
@ -598,7 +647,7 @@
|
|||
"dueDate": "Termin",
|
||||
"endDate": "Data zakończenia",
|
||||
"labels": "Etykiety",
|
||||
"percentDone": "% ukończenia",
|
||||
"percentDone": "Postęp",
|
||||
"priority": "Priorytet",
|
||||
"relatedTasks": "Powiązane zadania",
|
||||
"reminders": "Przypomnienia",
|
||||
|
@ -785,17 +834,20 @@
|
|||
"general": "Ogólne",
|
||||
"allPages": "Te skróty działają na wszystkich stronach.",
|
||||
"currentPageOnly": "Te skróty działają tylko na bieżącej stronie.",
|
||||
"somePagesOnly": "Te skróty działają tylko na niektórych stronach.",
|
||||
"toggleMenu": "Przełącz menu",
|
||||
"quickSearch": "Otwórz pasek wyszukiwania/szybkiej akcji",
|
||||
"then": "następnie",
|
||||
"task": {
|
||||
"title": "Widok zadań",
|
||||
"done": "Ukończone",
|
||||
"assign": "Przypisz do użytkownika",
|
||||
"done": "Oznacz zadanie jako wykonane / niewykonane",
|
||||
"assign": "Przypisz to zadanie do użytkownika",
|
||||
"labels": "Dodaj etykiety do tego zadania",
|
||||
"dueDate": "Zmień termin wykonania tego zadania",
|
||||
"attachment": "Dodaj załącznik do tego zadania",
|
||||
"related": "Zmodyfikuj zadania powiązane z tym zadaniem"
|
||||
"related": "Zmodyfikuj zadania powiązane z tym zadaniem",
|
||||
"color": "Zmień kolor tego zadania",
|
||||
"move": "Przenieś to zadanie do innej listy"
|
||||
},
|
||||
"list": {
|
||||
"title": "Widoki listy",
|
||||
|
@ -803,6 +855,14 @@
|
|||
"switchToGanttView": "Przełącz na widok Gantta",
|
||||
"switchToKanbanView": "Przełącz na widok Kanban",
|
||||
"switchToTableView": "Przełącz na widok tabeli"
|
||||
},
|
||||
"navigation": {
|
||||
"title": "Nawigacja",
|
||||
"overview": "Przejdź do przeglądu",
|
||||
"upcoming": "Przejdź do nadchodzących zadań",
|
||||
"namespaces": "Przejdź do sekcji i list",
|
||||
"labels": "Przejdź do etykiet",
|
||||
"teams": "Przejdź do zespołów"
|
||||
}
|
||||
},
|
||||
"update": {
|
||||
|
|
|
@ -385,7 +385,7 @@
|
|||
"showDoneTasks": "Show Done Tasks",
|
||||
"sortAlphabetically": "Sort Alphabetically",
|
||||
"enablePriority": "Enable Filter By Priority",
|
||||
"enablePercentDone": "Enable Filter By Percent Done",
|
||||
"enablePercentDone": "Enable Filter By Progress",
|
||||
"dueDateRange": "Due Date Range",
|
||||
"startDateRange": "Start Date Range",
|
||||
"endDateRange": "End Date Range",
|
||||
|
@ -485,7 +485,8 @@
|
|||
"showMenu": "Show the menu",
|
||||
"hideMenu": "Hide the menu",
|
||||
"forExample": "For example:",
|
||||
"welcomeBack": "Welcome Back!"
|
||||
"welcomeBack": "Welcome Back!",
|
||||
"custom": "Custom"
|
||||
},
|
||||
"input": {
|
||||
"resetColor": "Reset Color",
|
||||
|
@ -524,6 +525,57 @@
|
|||
"multiselect": {
|
||||
"createPlaceholder": "Create new",
|
||||
"selectPlaceholder": "Click or press enter to select"
|
||||
},
|
||||
"datepickerRange": {
|
||||
"to": "To",
|
||||
"from": "From",
|
||||
"fromto": "{from} to {to}",
|
||||
"ranges": {
|
||||
"today": "Today",
|
||||
"thisWeek": "This Week",
|
||||
"restOfThisWeek": "The Rest of This Week",
|
||||
"nextWeek": "Next Week",
|
||||
"next7Days": "Next 7 Days",
|
||||
"lastWeek": "Last Week",
|
||||
"thisMonth": "This Month",
|
||||
"restOfThisMonth": "The Rest of This Month",
|
||||
"nextMonth": "Next Month",
|
||||
"next30Days": "Next 30 Days",
|
||||
"lastMonth": "Last Month",
|
||||
"thisYear": "This Year",
|
||||
"restOfThisYear": "The Rest of This Year"
|
||||
}
|
||||
},
|
||||
"datemathHelp": {
|
||||
"canuse": "You can use date math to filter for relative dates.",
|
||||
"learnhow": "Check out how it works",
|
||||
"title": "Date Math",
|
||||
"intro": "Date Math allows you to specifiy relative dates which are resolved on the fly by Vikunja when applying the filter.",
|
||||
"expression": "Each Date Math expression starts with an anchor date, which can either be {0}, or a date string ending with {1}. This anchor date can optionally be followed by one or more maths expressions.",
|
||||
"similar": "These expressions are similar to the ones provided by {0} and {1}.",
|
||||
"add1Day": "Add one day",
|
||||
"minus1Day": "Subtract one day",
|
||||
"roundDay": "Round down to the nearest day",
|
||||
"supportedUnits": "Supported time units are:",
|
||||
"someExamples": "Some examples of time expressions:",
|
||||
"units": {
|
||||
"seconds": "Seconds",
|
||||
"minutes": "Minutes",
|
||||
"hours": "Hours",
|
||||
"days": "Days",
|
||||
"weeks": "Weeks",
|
||||
"months": "Months",
|
||||
"years": "Years"
|
||||
},
|
||||
"examples": {
|
||||
"now": "Right now",
|
||||
"in24h": "In 24h",
|
||||
"today": "Today at 00:00",
|
||||
"beginningOfThisWeek": "The beginning of this week at 00:00",
|
||||
"endOfThisWeek": "The end of this week",
|
||||
"in30Days": "In 30 days",
|
||||
"datePlusMonth": "{0} plus one month at 00:00 of that day"
|
||||
}
|
||||
}
|
||||
},
|
||||
"task": {
|
||||
|
@ -541,12 +593,9 @@
|
|||
"titleCurrent": "Current Tasks",
|
||||
"titleDates": "Tasks from {from} until {to}",
|
||||
"noDates": "Show tasks without dates",
|
||||
"current": "Current tasks",
|
||||
"from": "Tasks from",
|
||||
"until": "until",
|
||||
"today": "Today",
|
||||
"nextWeek": "Next Week",
|
||||
"nextMonth": "Next Month",
|
||||
"overdue": "Show overdue tasks",
|
||||
"fromuntil": "Tasks from {from} until {until}",
|
||||
"select": "Select a date range",
|
||||
"noTasks": "Nothing to do — Have a nice day!"
|
||||
},
|
||||
"detail": {
|
||||
|
@ -570,22 +619,22 @@
|
|||
"text2": "This will also remove all attachments, reminders and relations associated with this task and cannot be undone!"
|
||||
},
|
||||
"actions": {
|
||||
"assign": "Assign to a user",
|
||||
"label": "Add labels",
|
||||
"assign": "Assign to User",
|
||||
"label": "Add Labels",
|
||||
"priority": "Set Priority",
|
||||
"dueDate": "Set Due Date",
|
||||
"startDate": "Set a Start Date",
|
||||
"endDate": "Set an End Date",
|
||||
"startDate": "Set Start Date",
|
||||
"endDate": "Set End Date",
|
||||
"reminders": "Set Reminders",
|
||||
"repeatAfter": "Set a repeating interval",
|
||||
"percentDone": "Set Percent Done",
|
||||
"attachments": "Add attachments",
|
||||
"relatedTasks": "Add task relations",
|
||||
"moveList": "Move task",
|
||||
"color": "Set task color",
|
||||
"delete": "Delete task",
|
||||
"favorite": "Save as favorite",
|
||||
"unfavorite": "Remove from favorites"
|
||||
"repeatAfter": "Set Repeating Interval",
|
||||
"percentDone": "Set Progress",
|
||||
"attachments": "Add Attachments",
|
||||
"relatedTasks": "Add Relation",
|
||||
"moveList": "Move",
|
||||
"color": "Set Color",
|
||||
"delete": "Delete",
|
||||
"favorite": "Add to Favorites",
|
||||
"unfavorite": "Remove from Favorites"
|
||||
}
|
||||
},
|
||||
"attributes": {
|
||||
|
@ -598,7 +647,7 @@
|
|||
"dueDate": "Due Date",
|
||||
"endDate": "End Date",
|
||||
"labels": "Labels",
|
||||
"percentDone": "% Done",
|
||||
"percentDone": "Progress",
|
||||
"priority": "Priority",
|
||||
"relatedTasks": "Related Tasks",
|
||||
"reminders": "Reminders",
|
||||
|
@ -785,17 +834,20 @@
|
|||
"general": "General",
|
||||
"allPages": "These shortcuts work on all pages.",
|
||||
"currentPageOnly": "These shortcuts work only on the current page.",
|
||||
"somePagesOnly": "These shortcuts work only on some pages.",
|
||||
"toggleMenu": "Toggle The Menu",
|
||||
"quickSearch": "Open the search/quick action bar",
|
||||
"then": "then",
|
||||
"task": {
|
||||
"title": "Task Page",
|
||||
"done": "Done",
|
||||
"assign": "Assign to a user",
|
||||
"done": "Mark task done / undone",
|
||||
"assign": "Assign this task to a user",
|
||||
"labels": "Add labels to this task",
|
||||
"dueDate": "Change the due date of this task",
|
||||
"attachment": "Add an attachment to this task",
|
||||
"related": "Modify related tasks of this task"
|
||||
"related": "Modify related tasks of this task",
|
||||
"color": "Change the color of this task",
|
||||
"move": "Move this task to another list"
|
||||
},
|
||||
"list": {
|
||||
"title": "List Views",
|
||||
|
@ -803,6 +855,14 @@
|
|||
"switchToGanttView": "Switch to gantt view",
|
||||
"switchToKanbanView": "Switch to kanban view",
|
||||
"switchToTableView": "Switch to table view"
|
||||
},
|
||||
"navigation": {
|
||||
"title": "Navigation",
|
||||
"overview": "Navigato to overview",
|
||||
"upcoming": "Navigato to upcoming taks",
|
||||
"namespaces": "Navigate to namepaces & lists",
|
||||
"labels": "Navigate to labels",
|
||||
"teams": "Navigate to teams"
|
||||
}
|
||||
},
|
||||
"update": {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -385,7 +385,7 @@
|
|||
"showDoneTasks": "Show Done Tasks",
|
||||
"sortAlphabetically": "Sort Alphabetically",
|
||||
"enablePriority": "Enable Filter By Priority",
|
||||
"enablePercentDone": "Enable Filter By Percent Done",
|
||||
"enablePercentDone": "Enable Filter By Progress",
|
||||
"dueDateRange": "Due Date Range",
|
||||
"startDateRange": "Start Date Range",
|
||||
"endDateRange": "End Date Range",
|
||||
|
@ -485,7 +485,8 @@
|
|||
"showMenu": "Show the menu",
|
||||
"hideMenu": "Hide the menu",
|
||||
"forExample": "For example:",
|
||||
"welcomeBack": "Welcome Back!"
|
||||
"welcomeBack": "Welcome Back!",
|
||||
"custom": "Custom"
|
||||
},
|
||||
"input": {
|
||||
"resetColor": "Reset Color",
|
||||
|
@ -524,6 +525,57 @@
|
|||
"multiselect": {
|
||||
"createPlaceholder": "Create new",
|
||||
"selectPlaceholder": "Click or press enter to select"
|
||||
},
|
||||
"datepickerRange": {
|
||||
"to": "To",
|
||||
"from": "From",
|
||||
"fromto": "{from} to {to}",
|
||||
"ranges": {
|
||||
"today": "Today",
|
||||
"thisWeek": "This Week",
|
||||
"restOfThisWeek": "The Rest of This Week",
|
||||
"nextWeek": "Next Week",
|
||||
"next7Days": "Next 7 Days",
|
||||
"lastWeek": "Last Week",
|
||||
"thisMonth": "This Month",
|
||||
"restOfThisMonth": "The Rest of This Month",
|
||||
"nextMonth": "Next Month",
|
||||
"next30Days": "Next 30 Days",
|
||||
"lastMonth": "Last Month",
|
||||
"thisYear": "This Year",
|
||||
"restOfThisYear": "The Rest of This Year"
|
||||
}
|
||||
},
|
||||
"datemathHelp": {
|
||||
"canuse": "You can use date math to filter for relative dates.",
|
||||
"learnhow": "Check out how it works",
|
||||
"title": "Date Math",
|
||||
"intro": "Date Math allows you to specifiy relative dates which are resolved on the fly by Vikunja when applying the filter.",
|
||||
"expression": "Each Date Math expression starts with an anchor date, which can either be {0}, or a date string ending with {1}. This anchor date can optionally be followed by one or more maths expressions.",
|
||||
"similar": "These expressions are similar to the ones provided by {0} and {1}.",
|
||||
"add1Day": "Add one day",
|
||||
"minus1Day": "Subtract one day",
|
||||
"roundDay": "Round down to the nearest day",
|
||||
"supportedUnits": "Supported time units are:",
|
||||
"someExamples": "Some examples of time expressions:",
|
||||
"units": {
|
||||
"seconds": "Seconds",
|
||||
"minutes": "Minutes",
|
||||
"hours": "Hours",
|
||||
"days": "Days",
|
||||
"weeks": "Weeks",
|
||||
"months": "Months",
|
||||
"years": "Years"
|
||||
},
|
||||
"examples": {
|
||||
"now": "Right now",
|
||||
"in24h": "In 24h",
|
||||
"today": "Today at 00:00",
|
||||
"beginningOfThisWeek": "The beginning of this week at 00:00",
|
||||
"endOfThisWeek": "The end of this week",
|
||||
"in30Days": "In 30 days",
|
||||
"datePlusMonth": "{0} plus one month at 00:00 of that day"
|
||||
}
|
||||
}
|
||||
},
|
||||
"task": {
|
||||
|
@ -541,12 +593,9 @@
|
|||
"titleCurrent": "Current Tasks",
|
||||
"titleDates": "Tasks from {from} until {to}",
|
||||
"noDates": "Show tasks without dates",
|
||||
"current": "Current tasks",
|
||||
"from": "Tasks from",
|
||||
"until": "until",
|
||||
"today": "Today",
|
||||
"nextWeek": "Next Week",
|
||||
"nextMonth": "Next Month",
|
||||
"overdue": "Show overdue tasks",
|
||||
"fromuntil": "Tasks from {from} until {until}",
|
||||
"select": "Select a date range",
|
||||
"noTasks": "Nothing to do — Have a nice day!"
|
||||
},
|
||||
"detail": {
|
||||
|
@ -570,22 +619,22 @@
|
|||
"text2": "This will also remove all attachments, reminders and relations associated with this task and cannot be undone!"
|
||||
},
|
||||
"actions": {
|
||||
"assign": "Assign to a user",
|
||||
"label": "Add labels",
|
||||
"assign": "Assign to User",
|
||||
"label": "Add Labels",
|
||||
"priority": "Set Priority",
|
||||
"dueDate": "Set Due Date",
|
||||
"startDate": "Set a Start Date",
|
||||
"endDate": "Set an End Date",
|
||||
"startDate": "Set Start Date",
|
||||
"endDate": "Set End Date",
|
||||
"reminders": "Set Reminders",
|
||||
"repeatAfter": "Set a repeating interval",
|
||||
"percentDone": "Set Percent Done",
|
||||
"attachments": "Add attachments",
|
||||
"relatedTasks": "Add task relations",
|
||||
"moveList": "Move task",
|
||||
"color": "Set task color",
|
||||
"delete": "Delete task",
|
||||
"favorite": "Save as favorite",
|
||||
"unfavorite": "Remove from favorites"
|
||||
"repeatAfter": "Set Repeating Interval",
|
||||
"percentDone": "Set Progress",
|
||||
"attachments": "Add Attachments",
|
||||
"relatedTasks": "Add Relation",
|
||||
"moveList": "Move",
|
||||
"color": "Set Color",
|
||||
"delete": "Delete",
|
||||
"favorite": "Add to Favorites",
|
||||
"unfavorite": "Remove from Favorites"
|
||||
}
|
||||
},
|
||||
"attributes": {
|
||||
|
@ -598,7 +647,7 @@
|
|||
"dueDate": "Due Date",
|
||||
"endDate": "End Date",
|
||||
"labels": "Labels",
|
||||
"percentDone": "% Done",
|
||||
"percentDone": "Progress",
|
||||
"priority": "Priority",
|
||||
"relatedTasks": "Related Tasks",
|
||||
"reminders": "Reminders",
|
||||
|
@ -785,17 +834,20 @@
|
|||
"general": "General",
|
||||
"allPages": "These shortcuts work on all pages.",
|
||||
"currentPageOnly": "These shortcuts work only on the current page.",
|
||||
"somePagesOnly": "These shortcuts work only on some pages.",
|
||||
"toggleMenu": "Toggle The Menu",
|
||||
"quickSearch": "Open the search/quick action bar",
|
||||
"then": "then",
|
||||
"task": {
|
||||
"title": "Task Page",
|
||||
"done": "Done",
|
||||
"assign": "Assign to a user",
|
||||
"done": "Mark task done / undone",
|
||||
"assign": "Assign this task to a user",
|
||||
"labels": "Add labels to this task",
|
||||
"dueDate": "Change the due date of this task",
|
||||
"attachment": "Add an attachment to this task",
|
||||
"related": "Modify related tasks of this task"
|
||||
"related": "Modify related tasks of this task",
|
||||
"color": "Change the color of this task",
|
||||
"move": "Move this task to another list"
|
||||
},
|
||||
"list": {
|
||||
"title": "List Views",
|
||||
|
@ -803,6 +855,14 @@
|
|||
"switchToGanttView": "Switch to gantt view",
|
||||
"switchToKanbanView": "Switch to kanban view",
|
||||
"switchToTableView": "Switch to table view"
|
||||
},
|
||||
"navigation": {
|
||||
"title": "Navigation",
|
||||
"overview": "Navigato to overview",
|
||||
"upcoming": "Navigato to upcoming taks",
|
||||
"namespaces": "Navigate to namepaces & lists",
|
||||
"labels": "Navigate to labels",
|
||||
"teams": "Navigate to teams"
|
||||
}
|
||||
},
|
||||
"update": {
|
||||
|
|
|
@ -105,7 +105,7 @@
|
|||
"caldav": {
|
||||
"title": "CalDAV",
|
||||
"howTo": "Ты можешь подключить Vikunja к клиентам CalDAV, чтобы просматривать и управлять всеми задачами из разных клиентов. Введи этот URL в свой клиент:",
|
||||
"more": "Подробнее о caldav в Vikunja"
|
||||
"more": "Подробнее о CalDAV в Vikunja"
|
||||
},
|
||||
"avatar": {
|
||||
"title": "Аватар",
|
||||
|
@ -385,7 +385,7 @@
|
|||
"showDoneTasks": "Показывать завершённые задачи",
|
||||
"sortAlphabetically": "Sort Alphabetically",
|
||||
"enablePriority": "Вкл. фильтр по приоритету",
|
||||
"enablePercentDone": "По % завершения",
|
||||
"enablePercentDone": "Enable Filter By Progress",
|
||||
"dueDateRange": "Диапазон срока",
|
||||
"startDateRange": "Диапазон даты начала",
|
||||
"endDateRange": "Диапазон даты завершения",
|
||||
|
@ -485,7 +485,8 @@
|
|||
"showMenu": "Show the menu",
|
||||
"hideMenu": "Hide the menu",
|
||||
"forExample": "For example:",
|
||||
"welcomeBack": "Welcome Back!"
|
||||
"welcomeBack": "Welcome Back!",
|
||||
"custom": "Custom"
|
||||
},
|
||||
"input": {
|
||||
"resetColor": "Сбросить цвет",
|
||||
|
@ -524,6 +525,57 @@
|
|||
"multiselect": {
|
||||
"createPlaceholder": "Создать",
|
||||
"selectPlaceholder": "Кликните или нажмите Enter для выбора"
|
||||
},
|
||||
"datepickerRange": {
|
||||
"to": "To",
|
||||
"from": "From",
|
||||
"fromto": "{from} to {to}",
|
||||
"ranges": {
|
||||
"today": "Today",
|
||||
"thisWeek": "This Week",
|
||||
"restOfThisWeek": "The Rest of This Week",
|
||||
"nextWeek": "Next Week",
|
||||
"next7Days": "Next 7 Days",
|
||||
"lastWeek": "Last Week",
|
||||
"thisMonth": "This Month",
|
||||
"restOfThisMonth": "The Rest of This Month",
|
||||
"nextMonth": "Next Month",
|
||||
"next30Days": "Next 30 Days",
|
||||
"lastMonth": "Last Month",
|
||||
"thisYear": "This Year",
|
||||
"restOfThisYear": "The Rest of This Year"
|
||||
}
|
||||
},
|
||||
"datemathHelp": {
|
||||
"canuse": "You can use date math to filter for relative dates.",
|
||||
"learnhow": "Check out how it works",
|
||||
"title": "Date Math",
|
||||
"intro": "Date Math allows you to specifiy relative dates which are resolved on the fly by Vikunja when applying the filter.",
|
||||
"expression": "Each Date Math expression starts with an anchor date, which can either be {0}, or a date string ending with {1}. This anchor date can optionally be followed by one or more maths expressions.",
|
||||
"similar": "These expressions are similar to the ones provided by {0} and {1}.",
|
||||
"add1Day": "Add one day",
|
||||
"minus1Day": "Subtract one day",
|
||||
"roundDay": "Round down to the nearest day",
|
||||
"supportedUnits": "Supported time units are:",
|
||||
"someExamples": "Some examples of time expressions:",
|
||||
"units": {
|
||||
"seconds": "Seconds",
|
||||
"minutes": "Minutes",
|
||||
"hours": "Hours",
|
||||
"days": "Days",
|
||||
"weeks": "Weeks",
|
||||
"months": "Months",
|
||||
"years": "Years"
|
||||
},
|
||||
"examples": {
|
||||
"now": "Right now",
|
||||
"in24h": "In 24h",
|
||||
"today": "Today at 00:00",
|
||||
"beginningOfThisWeek": "The beginning of this week at 00:00",
|
||||
"endOfThisWeek": "The end of this week",
|
||||
"in30Days": "In 30 days",
|
||||
"datePlusMonth": "{0} plus one month at 00:00 of that day"
|
||||
}
|
||||
}
|
||||
},
|
||||
"task": {
|
||||
|
@ -541,12 +593,9 @@
|
|||
"titleCurrent": "Текущие задачи",
|
||||
"titleDates": "Задачи с {from} по {to}",
|
||||
"noDates": "Показать задачи без даты",
|
||||
"current": "Текущие задачи",
|
||||
"from": "Задачи с",
|
||||
"until": "по",
|
||||
"today": "Сегодня",
|
||||
"nextWeek": "Неделя",
|
||||
"nextMonth": "Месяц",
|
||||
"overdue": "Show overdue tasks",
|
||||
"fromuntil": "Tasks from {from} until {until}",
|
||||
"select": "Select a date range",
|
||||
"noTasks": "Nothing to do — Have a nice day!"
|
||||
},
|
||||
"detail": {
|
||||
|
@ -570,22 +619,22 @@
|
|||
"text2": "Будут удалены все вложения, напоминания и отношения, связанные с этой задачей, и отменить это будет нельзя!"
|
||||
},
|
||||
"actions": {
|
||||
"assign": "Assign to a user",
|
||||
"label": "Добавить метки",
|
||||
"assign": "Assign to User",
|
||||
"label": "Add Labels",
|
||||
"priority": "Установить приоритет",
|
||||
"dueDate": "Установить срок",
|
||||
"startDate": "Установить дату начала",
|
||||
"endDate": "Установить дату завершения",
|
||||
"startDate": "Set Start Date",
|
||||
"endDate": "Set End Date",
|
||||
"reminders": "Установить напоминания",
|
||||
"repeatAfter": "Установить интервал повтора",
|
||||
"percentDone": "Установить процент завершения",
|
||||
"attachments": "Добавить вложения",
|
||||
"relatedTasks": "Добавить связанные задачи",
|
||||
"moveList": "Переместить задачу",
|
||||
"color": "Установить цвет задачи",
|
||||
"delete": "Удалить задачу",
|
||||
"favorite": "Сохранить как ибзранное",
|
||||
"unfavorite": "Убрать из избранного"
|
||||
"repeatAfter": "Set Repeating Interval",
|
||||
"percentDone": "Set Progress",
|
||||
"attachments": "Add Attachments",
|
||||
"relatedTasks": "Add Relation",
|
||||
"moveList": "Move",
|
||||
"color": "Set Color",
|
||||
"delete": "Delete",
|
||||
"favorite": "Add to Favorites",
|
||||
"unfavorite": "Remove from Favorites"
|
||||
}
|
||||
},
|
||||
"attributes": {
|
||||
|
@ -598,7 +647,7 @@
|
|||
"dueDate": "Срок",
|
||||
"endDate": "Дата завершения",
|
||||
"labels": "Метки",
|
||||
"percentDone": "% Завершено",
|
||||
"percentDone": "Progress",
|
||||
"priority": "Приоритет",
|
||||
"relatedTasks": "Связанные задачи",
|
||||
"reminders": "Напоминания",
|
||||
|
@ -785,17 +834,20 @@
|
|||
"general": "General",
|
||||
"allPages": "Работают на всех страницах.",
|
||||
"currentPageOnly": "Работают только на текущей странице.",
|
||||
"somePagesOnly": "These shortcuts work only on some pages.",
|
||||
"toggleMenu": "Переключить меню",
|
||||
"quickSearch": "Открыть панель поиска/быстрых действий",
|
||||
"then": "then",
|
||||
"task": {
|
||||
"title": "Страница задачи",
|
||||
"done": "Done",
|
||||
"assign": "Assign to a user",
|
||||
"done": "Mark task done / undone",
|
||||
"assign": "Assign this task to a user",
|
||||
"labels": "Добавить метки этой задаче",
|
||||
"dueDate": "Изменить срок этой задачи",
|
||||
"attachment": "Добавить вложение к задаче",
|
||||
"related": "Изменить связанные задачи"
|
||||
"related": "Изменить связанные задачи",
|
||||
"color": "Change the color of this task",
|
||||
"move": "Move this task to another list"
|
||||
},
|
||||
"list": {
|
||||
"title": "List Views",
|
||||
|
@ -803,6 +855,14 @@
|
|||
"switchToGanttView": "Switch to gantt view",
|
||||
"switchToKanbanView": "Switch to kanban view",
|
||||
"switchToTableView": "Switch to table view"
|
||||
},
|
||||
"navigation": {
|
||||
"title": "Navigation",
|
||||
"overview": "Navigato to overview",
|
||||
"upcoming": "Navigato to upcoming taks",
|
||||
"namespaces": "Navigate to namepaces & lists",
|
||||
"labels": "Navigate to labels",
|
||||
"teams": "Navigate to teams"
|
||||
}
|
||||
},
|
||||
"update": {
|
||||
|
|
|
@ -385,7 +385,7 @@
|
|||
"showDoneTasks": "Show Done Tasks",
|
||||
"sortAlphabetically": "Sort Alphabetically",
|
||||
"enablePriority": "Enable Filter By Priority",
|
||||
"enablePercentDone": "Enable Filter By Percent Done",
|
||||
"enablePercentDone": "Enable Filter By Progress",
|
||||
"dueDateRange": "Due Date Range",
|
||||
"startDateRange": "Start Date Range",
|
||||
"endDateRange": "End Date Range",
|
||||
|
@ -485,7 +485,8 @@
|
|||
"showMenu": "Show the menu",
|
||||
"hideMenu": "Hide the menu",
|
||||
"forExample": "For example:",
|
||||
"welcomeBack": "Welcome Back!"
|
||||
"welcomeBack": "Welcome Back!",
|
||||
"custom": "Custom"
|
||||
},
|
||||
"input": {
|
||||
"resetColor": "Reset Color",
|
||||
|
@ -524,6 +525,57 @@
|
|||
"multiselect": {
|
||||
"createPlaceholder": "Create new",
|
||||
"selectPlaceholder": "Click or press enter to select"
|
||||
},
|
||||
"datepickerRange": {
|
||||
"to": "To",
|
||||
"from": "From",
|
||||
"fromto": "{from} to {to}",
|
||||
"ranges": {
|
||||
"today": "Today",
|
||||
"thisWeek": "This Week",
|
||||
"restOfThisWeek": "The Rest of This Week",
|
||||
"nextWeek": "Next Week",
|
||||
"next7Days": "Next 7 Days",
|
||||
"lastWeek": "Last Week",
|
||||
"thisMonth": "This Month",
|
||||
"restOfThisMonth": "The Rest of This Month",
|
||||
"nextMonth": "Next Month",
|
||||
"next30Days": "Next 30 Days",
|
||||
"lastMonth": "Last Month",
|
||||
"thisYear": "This Year",
|
||||
"restOfThisYear": "The Rest of This Year"
|
||||
}
|
||||
},
|
||||
"datemathHelp": {
|
||||
"canuse": "You can use date math to filter for relative dates.",
|
||||
"learnhow": "Check out how it works",
|
||||
"title": "Date Math",
|
||||
"intro": "Date Math allows you to specifiy relative dates which are resolved on the fly by Vikunja when applying the filter.",
|
||||
"expression": "Each Date Math expression starts with an anchor date, which can either be {0}, or a date string ending with {1}. This anchor date can optionally be followed by one or more maths expressions.",
|
||||
"similar": "These expressions are similar to the ones provided by {0} and {1}.",
|
||||
"add1Day": "Add one day",
|
||||
"minus1Day": "Subtract one day",
|
||||
"roundDay": "Round down to the nearest day",
|
||||
"supportedUnits": "Supported time units are:",
|
||||
"someExamples": "Some examples of time expressions:",
|
||||
"units": {
|
||||
"seconds": "Seconds",
|
||||
"minutes": "Minutes",
|
||||
"hours": "Hours",
|
||||
"days": "Days",
|
||||
"weeks": "Weeks",
|
||||
"months": "Months",
|
||||
"years": "Years"
|
||||
},
|
||||
"examples": {
|
||||
"now": "Right now",
|
||||
"in24h": "In 24h",
|
||||
"today": "Today at 00:00",
|
||||
"beginningOfThisWeek": "The beginning of this week at 00:00",
|
||||
"endOfThisWeek": "The end of this week",
|
||||
"in30Days": "In 30 days",
|
||||
"datePlusMonth": "{0} plus one month at 00:00 of that day"
|
||||
}
|
||||
}
|
||||
},
|
||||
"task": {
|
||||
|
@ -541,12 +593,9 @@
|
|||
"titleCurrent": "Current Tasks",
|
||||
"titleDates": "Tasks from {from} until {to}",
|
||||
"noDates": "Show tasks without dates",
|
||||
"current": "Current tasks",
|
||||
"from": "Tasks from",
|
||||
"until": "until",
|
||||
"today": "Today",
|
||||
"nextWeek": "Next Week",
|
||||
"nextMonth": "Next Month",
|
||||
"overdue": "Show overdue tasks",
|
||||
"fromuntil": "Tasks from {from} until {until}",
|
||||
"select": "Select a date range",
|
||||
"noTasks": "Nothing to do — Have a nice day!"
|
||||
},
|
||||
"detail": {
|
||||
|
@ -570,22 +619,22 @@
|
|||
"text2": "This will also remove all attachments, reminders and relations associated with this task and cannot be undone!"
|
||||
},
|
||||
"actions": {
|
||||
"assign": "Assign to a user",
|
||||
"label": "Add labels",
|
||||
"assign": "Assign to User",
|
||||
"label": "Add Labels",
|
||||
"priority": "Set Priority",
|
||||
"dueDate": "Set Due Date",
|
||||
"startDate": "Set a Start Date",
|
||||
"endDate": "Set an End Date",
|
||||
"startDate": "Set Start Date",
|
||||
"endDate": "Set End Date",
|
||||
"reminders": "Set Reminders",
|
||||
"repeatAfter": "Set a repeating interval",
|
||||
"percentDone": "Set Percent Done",
|
||||
"attachments": "Add attachments",
|
||||
"relatedTasks": "Add task relations",
|
||||
"moveList": "Move task",
|
||||
"color": "Set task color",
|
||||
"delete": "Delete task",
|
||||
"favorite": "Save as favorite",
|
||||
"unfavorite": "Remove from favorites"
|
||||
"repeatAfter": "Set Repeating Interval",
|
||||
"percentDone": "Set Progress",
|
||||
"attachments": "Add Attachments",
|
||||
"relatedTasks": "Add Relation",
|
||||
"moveList": "Move",
|
||||
"color": "Set Color",
|
||||
"delete": "Delete",
|
||||
"favorite": "Add to Favorites",
|
||||
"unfavorite": "Remove from Favorites"
|
||||
}
|
||||
},
|
||||
"attributes": {
|
||||
|
@ -598,7 +647,7 @@
|
|||
"dueDate": "Due Date",
|
||||
"endDate": "End Date",
|
||||
"labels": "Labels",
|
||||
"percentDone": "% Done",
|
||||
"percentDone": "Progress",
|
||||
"priority": "Priority",
|
||||
"relatedTasks": "Related Tasks",
|
||||
"reminders": "Reminders",
|
||||
|
@ -785,17 +834,20 @@
|
|||
"general": "General",
|
||||
"allPages": "These shortcuts work on all pages.",
|
||||
"currentPageOnly": "These shortcuts work only on the current page.",
|
||||
"somePagesOnly": "These shortcuts work only on some pages.",
|
||||
"toggleMenu": "Toggle The Menu",
|
||||
"quickSearch": "Open the search/quick action bar",
|
||||
"then": "then",
|
||||
"task": {
|
||||
"title": "Task Page",
|
||||
"done": "Done",
|
||||
"assign": "Assign to a user",
|
||||
"done": "Mark task done / undone",
|
||||
"assign": "Assign this task to a user",
|
||||
"labels": "Add labels to this task",
|
||||
"dueDate": "Change the due date of this task",
|
||||
"attachment": "Add an attachment to this task",
|
||||
"related": "Modify related tasks of this task"
|
||||
"related": "Modify related tasks of this task",
|
||||
"color": "Change the color of this task",
|
||||
"move": "Move this task to another list"
|
||||
},
|
||||
"list": {
|
||||
"title": "List Views",
|
||||
|
@ -803,6 +855,14 @@
|
|||
"switchToGanttView": "Switch to gantt view",
|
||||
"switchToKanbanView": "Switch to kanban view",
|
||||
"switchToTableView": "Switch to table view"
|
||||
},
|
||||
"navigation": {
|
||||
"title": "Navigation",
|
||||
"overview": "Navigato to overview",
|
||||
"upcoming": "Navigato to upcoming taks",
|
||||
"namespaces": "Navigate to namepaces & lists",
|
||||
"labels": "Navigate to labels",
|
||||
"teams": "Navigate to teams"
|
||||
}
|
||||
},
|
||||
"update": {
|
||||
|
|
|
@ -385,7 +385,7 @@
|
|||
"showDoneTasks": "Show Done Tasks",
|
||||
"sortAlphabetically": "Sort Alphabetically",
|
||||
"enablePriority": "Enable Filter By Priority",
|
||||
"enablePercentDone": "Enable Filter By Percent Done",
|
||||
"enablePercentDone": "Enable Filter By Progress",
|
||||
"dueDateRange": "Due Date Range",
|
||||
"startDateRange": "Start Date Range",
|
||||
"endDateRange": "End Date Range",
|
||||
|
@ -485,7 +485,8 @@
|
|||
"showMenu": "Show the menu",
|
||||
"hideMenu": "Hide the menu",
|
||||
"forExample": "For example:",
|
||||
"welcomeBack": "Welcome Back!"
|
||||
"welcomeBack": "Welcome Back!",
|
||||
"custom": "Custom"
|
||||
},
|
||||
"input": {
|
||||
"resetColor": "Reset Color",
|
||||
|
@ -524,6 +525,57 @@
|
|||
"multiselect": {
|
||||
"createPlaceholder": "Create new",
|
||||
"selectPlaceholder": "Click or press enter to select"
|
||||
},
|
||||
"datepickerRange": {
|
||||
"to": "To",
|
||||
"from": "From",
|
||||
"fromto": "{from} to {to}",
|
||||
"ranges": {
|
||||
"today": "Today",
|
||||
"thisWeek": "This Week",
|
||||
"restOfThisWeek": "The Rest of This Week",
|
||||
"nextWeek": "Next Week",
|
||||
"next7Days": "Next 7 Days",
|
||||
"lastWeek": "Last Week",
|
||||
"thisMonth": "This Month",
|
||||
"restOfThisMonth": "The Rest of This Month",
|
||||
"nextMonth": "Next Month",
|
||||
"next30Days": "Next 30 Days",
|
||||
"lastMonth": "Last Month",
|
||||
"thisYear": "This Year",
|
||||
"restOfThisYear": "The Rest of This Year"
|
||||
}
|
||||
},
|
||||
"datemathHelp": {
|
||||
"canuse": "You can use date math to filter for relative dates.",
|
||||
"learnhow": "Check out how it works",
|
||||
"title": "Date Math",
|
||||
"intro": "Date Math allows you to specifiy relative dates which are resolved on the fly by Vikunja when applying the filter.",
|
||||
"expression": "Each Date Math expression starts with an anchor date, which can either be {0}, or a date string ending with {1}. This anchor date can optionally be followed by one or more maths expressions.",
|
||||
"similar": "These expressions are similar to the ones provided by {0} and {1}.",
|
||||
"add1Day": "Add one day",
|
||||
"minus1Day": "Subtract one day",
|
||||
"roundDay": "Round down to the nearest day",
|
||||
"supportedUnits": "Supported time units are:",
|
||||
"someExamples": "Some examples of time expressions:",
|
||||
"units": {
|
||||
"seconds": "Seconds",
|
||||
"minutes": "Minutes",
|
||||
"hours": "Hours",
|
||||
"days": "Days",
|
||||
"weeks": "Weeks",
|
||||
"months": "Months",
|
||||
"years": "Years"
|
||||
},
|
||||
"examples": {
|
||||
"now": "Right now",
|
||||
"in24h": "In 24h",
|
||||
"today": "Today at 00:00",
|
||||
"beginningOfThisWeek": "The beginning of this week at 00:00",
|
||||
"endOfThisWeek": "The end of this week",
|
||||
"in30Days": "In 30 days",
|
||||
"datePlusMonth": "{0} plus one month at 00:00 of that day"
|
||||
}
|
||||
}
|
||||
},
|
||||
"task": {
|
||||
|
@ -541,12 +593,9 @@
|
|||
"titleCurrent": "Current Tasks",
|
||||
"titleDates": "Tasks from {from} until {to}",
|
||||
"noDates": "Show tasks without dates",
|
||||
"current": "Current tasks",
|
||||
"from": "Tasks from",
|
||||
"until": "until",
|
||||
"today": "Today",
|
||||
"nextWeek": "Next Week",
|
||||
"nextMonth": "Next Month",
|
||||
"overdue": "Show overdue tasks",
|
||||
"fromuntil": "Tasks from {from} until {until}",
|
||||
"select": "Select a date range",
|
||||
"noTasks": "Nothing to do — Have a nice day!"
|
||||
},
|
||||
"detail": {
|
||||
|
@ -570,22 +619,22 @@
|
|||
"text2": "This will also remove all attachments, reminders and relations associated with this task and cannot be undone!"
|
||||
},
|
||||
"actions": {
|
||||
"assign": "Assign to a user",
|
||||
"label": "Add labels",
|
||||
"assign": "Assign to User",
|
||||
"label": "Add Labels",
|
||||
"priority": "Set Priority",
|
||||
"dueDate": "Set Due Date",
|
||||
"startDate": "Set a Start Date",
|
||||
"endDate": "Set an End Date",
|
||||
"startDate": "Set Start Date",
|
||||
"endDate": "Set End Date",
|
||||
"reminders": "Set Reminders",
|
||||
"repeatAfter": "Set a repeating interval",
|
||||
"percentDone": "Set Percent Done",
|
||||
"attachments": "Add attachments",
|
||||
"relatedTasks": "Add task relations",
|
||||
"moveList": "Move task",
|
||||
"color": "Set task color",
|
||||
"delete": "Delete task",
|
||||
"favorite": "Save as favorite",
|
||||
"unfavorite": "Remove from favorites"
|
||||
"repeatAfter": "Set Repeating Interval",
|
||||
"percentDone": "Set Progress",
|
||||
"attachments": "Add Attachments",
|
||||
"relatedTasks": "Add Relation",
|
||||
"moveList": "Move",
|
||||
"color": "Set Color",
|
||||
"delete": "Delete",
|
||||
"favorite": "Add to Favorites",
|
||||
"unfavorite": "Remove from Favorites"
|
||||
}
|
||||
},
|
||||
"attributes": {
|
||||
|
@ -598,7 +647,7 @@
|
|||
"dueDate": "Due Date",
|
||||
"endDate": "End Date",
|
||||
"labels": "Labels",
|
||||
"percentDone": "% Done",
|
||||
"percentDone": "Progress",
|
||||
"priority": "Priority",
|
||||
"relatedTasks": "Related Tasks",
|
||||
"reminders": "Reminders",
|
||||
|
@ -785,17 +834,20 @@
|
|||
"general": "General",
|
||||
"allPages": "These shortcuts work on all pages.",
|
||||
"currentPageOnly": "These shortcuts work only on the current page.",
|
||||
"somePagesOnly": "These shortcuts work only on some pages.",
|
||||
"toggleMenu": "Toggle The Menu",
|
||||
"quickSearch": "Open the search/quick action bar",
|
||||
"then": "then",
|
||||
"task": {
|
||||
"title": "Task Page",
|
||||
"done": "Done",
|
||||
"assign": "Assign to a user",
|
||||
"done": "Mark task done / undone",
|
||||
"assign": "Assign this task to a user",
|
||||
"labels": "Add labels to this task",
|
||||
"dueDate": "Change the due date of this task",
|
||||
"attachment": "Add an attachment to this task",
|
||||
"related": "Modify related tasks of this task"
|
||||
"related": "Modify related tasks of this task",
|
||||
"color": "Change the color of this task",
|
||||
"move": "Move this task to another list"
|
||||
},
|
||||
"list": {
|
||||
"title": "List Views",
|
||||
|
@ -803,6 +855,14 @@
|
|||
"switchToGanttView": "Switch to gantt view",
|
||||
"switchToKanbanView": "Switch to kanban view",
|
||||
"switchToTableView": "Switch to table view"
|
||||
},
|
||||
"navigation": {
|
||||
"title": "Navigation",
|
||||
"overview": "Navigato to overview",
|
||||
"upcoming": "Navigato to upcoming taks",
|
||||
"namespaces": "Navigate to namepaces & lists",
|
||||
"labels": "Navigate to labels",
|
||||
"teams": "Navigate to teams"
|
||||
}
|
||||
},
|
||||
"update": {
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
"lastViewed": "Xem gần đây",
|
||||
"list": {
|
||||
"newText": "Bạn có thể tạo một danh sách công việc mới cho mình:",
|
||||
"new": "New list",
|
||||
"new": "Danh sách mới",
|
||||
"importText": "Hoặc nhập danh sách và nhiệm vụ của bạn từ các dịch vụ khác vào Vikunja:",
|
||||
"import": "Nhập dữ liệu của bạn vào Vikunja"
|
||||
}
|
||||
|
@ -31,7 +31,7 @@
|
|||
"username": "Tên người dùng",
|
||||
"usernameEmail": "Tên người dùng hoặc Email",
|
||||
"usernamePlaceholder": "ví dụ: frederick",
|
||||
"email": "Email address",
|
||||
"email": "Địa chỉ Email",
|
||||
"emailPlaceholder": "ví dụ: frederic{'@'}vikunja.io",
|
||||
"password": "Mật khẩu",
|
||||
"passwordPlaceholder": "ví dụ: •••••••••••",
|
||||
|
@ -44,20 +44,20 @@
|
|||
"totpTitle": "Mã xác thực hai lớp",
|
||||
"totpPlaceholder": "ví dụ: 123456",
|
||||
"login": "Đăng nhập",
|
||||
"createAccount": "Create account",
|
||||
"createAccount": "Tạo tài khoản",
|
||||
"loginWith": "Đăng nhập với {provider}",
|
||||
"authenticating": "Đang xác thực…",
|
||||
"openIdStateError": "Trạng thái không khớp, từ chối tiếp tục!",
|
||||
"openIdGeneralError": "Đã xảy ra lỗi khi xác thực với bên thứ ba.",
|
||||
"logout": "Đăng xuất",
|
||||
"emailInvalid": "Please enter a valid email address.",
|
||||
"usernameRequired": "Please provide a username.",
|
||||
"passwordRequired": "Please provide a password.",
|
||||
"showPassword": "Show the password",
|
||||
"hidePassword": "Hide the password",
|
||||
"noAccountYet": "Don't have an account yet?",
|
||||
"alreadyHaveAnAccount": "Already have an account?",
|
||||
"remember": "Stay logged in"
|
||||
"emailInvalid": "Vui lòng nhập một địa chỉ email hợp lệ.",
|
||||
"usernameRequired": "Vui lòng cung cấp tên người dùng.",
|
||||
"passwordRequired": "Vui lòng cung cấp một mật khẩu.",
|
||||
"showPassword": "Hiển thị mật khẩu",
|
||||
"hidePassword": "Ẩn mật khẩu",
|
||||
"noAccountYet": "Bạn chưa có tài khoản?",
|
||||
"alreadyHaveAnAccount": "Đã có tài khoản?",
|
||||
"remember": "Duy trì đăng nhập"
|
||||
},
|
||||
"settings": {
|
||||
"title": "Cài đặt",
|
||||
|
@ -68,7 +68,7 @@
|
|||
"currentPasswordPlaceholder": "Nhập mật khẩu hiện tại của bạn",
|
||||
"passwordsDontMatch": "Mật khẩu mới và xác nhận của nó không khớp.",
|
||||
"passwordUpdateSuccess": "Mật khẩu đã được cập nhật thành công.",
|
||||
"updateEmailTitle": "Update Your Email Address",
|
||||
"updateEmailTitle": "Cập nhật địa chỉ e-mail của bạn",
|
||||
"updateEmailNew": "Địa chỉ email mới",
|
||||
"updateEmailSuccess": "Địa chỉ email của bạn đã được cập nhật thành công. Chúng tôi đã gửi cho bạn một liên kết để xác nhận nó.",
|
||||
"general": {
|
||||
|
@ -86,7 +86,7 @@
|
|||
"weekStartMonday": "Thứ hai",
|
||||
"language": "Ngôn ngữ",
|
||||
"defaultList": "Danh sách mặc định",
|
||||
"timezone": "Time Zone"
|
||||
"timezone": "Múi giờ"
|
||||
},
|
||||
"totp": {
|
||||
"title": "Xác thực hai lớp",
|
||||
|
@ -103,7 +103,7 @@
|
|||
"disableSuccess": "Xác thực hai lớp đã bị vô hiệu hóa thành công."
|
||||
},
|
||||
"caldav": {
|
||||
"title": "Giao thức Caldav",
|
||||
"title": "Giao thức CalDAV",
|
||||
"howTo": "Bạn có thể kết nối Vikunja tới các máy khách CalDAV để xem và quản lý tất cả các công việc từ nhiều máy khách khác nhau. Nhập URL này vào ứng dụng khách của bạn:",
|
||||
"more": "Tìm hiểu thêm về CalDAV"
|
||||
},
|
||||
|
@ -165,7 +165,7 @@
|
|||
"searchSelect": "Nhấp hoặc nhấn enter để chọn danh sách này",
|
||||
"shared": "Đang tham gia",
|
||||
"create": {
|
||||
"header": "New list",
|
||||
"header": "Danh sách mới",
|
||||
"titlePlaceholder": "Tên danh sách ở đây…",
|
||||
"addTitleRequired": "Hãy xác định một tên.",
|
||||
"createdSuccess": "Danh sách đã được tạo thành công.",
|
||||
|
@ -323,7 +323,7 @@
|
|||
"namespaces": "Góc làm việc",
|
||||
"search": "Gõ để tìm kiếm một góc làm việc…",
|
||||
"create": {
|
||||
"title": "New namespace",
|
||||
"title": "Góc làm việc mới",
|
||||
"titleRequired": "Hãy đặt một tiêu đề.",
|
||||
"explanation": "Góc làm việc là một tập hợp các danh sách mà bạn có thể chia sẻ và sử dụng để sắp xếp các danh sách của mình. Trên thực tế, mọi danh sách đều thuộc về một góc làm việc.",
|
||||
"tooltip": "Góc làm việc là gì?",
|
||||
|
@ -335,7 +335,7 @@
|
|||
"archiveText": "Bạn sẽ không thể chỉnh sửa góc làm việc này hoặc tạo danh sách mới cho đến khi bạn bỏ lưu trữ nó. Điều này cũng sẽ lưu trữ tất cả các danh sách trong góc làm việc này.",
|
||||
"unarchiveText": "Bạn có thể tạo danh sách mới hoặc chỉnh sửa nó.",
|
||||
"success": "Góc làm việc đã lưu trữ thành công.",
|
||||
"unarchiveSuccess": "The namespace was successfully un-archived.",
|
||||
"unarchiveSuccess": "Góc làm việc đã được bỏ lưu trữ thành công.",
|
||||
"description": "Nếu một góc làm việc được lưu trữ, bạn không thể tạo thêm danh sách hoặc chỉnh sửa nó."
|
||||
},
|
||||
"delete": {
|
||||
|
@ -385,14 +385,14 @@
|
|||
"showDoneTasks": "Hiển thị các công việc đã hoàn thành",
|
||||
"sortAlphabetically": "Xếp theo bảng chữ cái",
|
||||
"enablePriority": "Bật Bộ lọc theo mức độ ưu tiên",
|
||||
"enablePercentDone": "Bật Bộ lọc theo tỉ lệ % hoàn thành",
|
||||
"enablePercentDone": "Bật Bộ lọc theo Tiến độ",
|
||||
"dueDateRange": "Phạm vi ngày đến hạn",
|
||||
"startDateRange": "Phạm vi Ngày bắt đầu",
|
||||
"endDateRange": "Phạm vi Ngày Kết thúc",
|
||||
"reminderRange": "Phạm vi Ngày nhắc nhở"
|
||||
},
|
||||
"create": {
|
||||
"title": "New Saved Filter",
|
||||
"title": "Bộ lọc đã lưu mới",
|
||||
"description": "Bộ lọc sẵn là một danh sách ảo được chọn từ một tập hợp các bộ lọc. Sau khi được tạo, nó sẽ xuất hiện trong một không gian làm việc đặc biệt.",
|
||||
"action": "Tạo thêm bộ lọc sẵn"
|
||||
},
|
||||
|
@ -485,7 +485,8 @@
|
|||
"showMenu": "Hiển thị menu",
|
||||
"hideMenu": "Ẩn menu",
|
||||
"forExample": "Ví dụ:",
|
||||
"welcomeBack": "Mừng quá! Bạn trở lại rồi."
|
||||
"welcomeBack": "Mừng quá! Bạn trở lại rồi.",
|
||||
"custom": "Tuỳ chỉnh"
|
||||
},
|
||||
"input": {
|
||||
"resetColor": "Đặt lại màu",
|
||||
|
@ -524,6 +525,57 @@
|
|||
"multiselect": {
|
||||
"createPlaceholder": "Tạo mới",
|
||||
"selectPlaceholder": "Nhấp hoặc nhấn enter để chọn"
|
||||
},
|
||||
"datepickerRange": {
|
||||
"to": "Đến",
|
||||
"from": "Từ",
|
||||
"fromto": "{from} đến {to}",
|
||||
"ranges": {
|
||||
"today": "Hôm nay",
|
||||
"thisWeek": "Tuần này",
|
||||
"restOfThisWeek": "Toàn bộ ngày còn lại trong tuần này",
|
||||
"nextWeek": "Tuần tới",
|
||||
"next7Days": "7 ngày tới",
|
||||
"lastWeek": "Tuần trước",
|
||||
"thisMonth": "Tháng này",
|
||||
"restOfThisMonth": "Toàn bộ ngày còn lại trong tháng này",
|
||||
"nextMonth": "Tháng tới",
|
||||
"next30Days": "30 ngày tới",
|
||||
"lastMonth": "Tháng trước",
|
||||
"thisYear": "Năm nay",
|
||||
"restOfThisYear": "Toàn bộ ngày còn lại trong năm"
|
||||
}
|
||||
},
|
||||
"datemathHelp": {
|
||||
"canuse": "Bạn có thể sử dụng biểu thức tính ngày để lọc những ngày liên quan.",
|
||||
"learnhow": "Xem cách hoạt động",
|
||||
"title": "Tính Ngày",
|
||||
"intro": "Tính Ngày cho phép bạn xác định những ngày liên quan được xử lý bởi Vikunja khi áp dụng bộ lọc.",
|
||||
"expression": "Mỗi Biểu thức tính ngày bắt đầu bằng một ngày cố định, có thể là {0}, hoặc kết thúc bằng {1}. Ngày cố định này có thể được theo sau bởi một hoặc nhiều biểu thức toán học.",
|
||||
"similar": "Những biểu thức này tương tự như những biểu thức được cung cấp bởi {0} và {1}.",
|
||||
"add1Day": "Thêm một ngày",
|
||||
"minus1Day": "Bớt đi một ngày",
|
||||
"roundDay": "Làm tròn đến ngày gần nhất",
|
||||
"supportedUnits": "Các đơn vị thời gian hỗ trợ là:",
|
||||
"someExamples": "Một vài ví dụ về cách hiển thị thời gian:",
|
||||
"units": {
|
||||
"seconds": "Giây",
|
||||
"minutes": "Phút",
|
||||
"hours": "Giờ",
|
||||
"days": "Ngày",
|
||||
"weeks": "Tuần",
|
||||
"months": "Tháng",
|
||||
"years": "Năm"
|
||||
},
|
||||
"examples": {
|
||||
"now": "Ngay bây giờ",
|
||||
"in24h": "Trong vòng 24h",
|
||||
"today": "Hôm nay lúc 00:00",
|
||||
"beginningOfThisWeek": "Bắt đầu tuần này lúc 00:00",
|
||||
"endOfThisWeek": "Cuối tuần này",
|
||||
"in30Days": "Trong 30 ngày",
|
||||
"datePlusMonth": "{0} thêm một tháng kể từ lúc 00:00 ngày hôm đó"
|
||||
}
|
||||
}
|
||||
},
|
||||
"task": {
|
||||
|
@ -541,12 +593,9 @@
|
|||
"titleCurrent": "Công việc hiện tại",
|
||||
"titleDates": "Công việc từ {from} cho đến {to}",
|
||||
"noDates": "Hiển thị công việc không có ngày tháng",
|
||||
"current": "Công việc hiện tại",
|
||||
"from": "Công việc từ",
|
||||
"until": "cho đến",
|
||||
"today": "Hôm nay",
|
||||
"nextWeek": "Tuần tới",
|
||||
"nextMonth": "Tháng tới",
|
||||
"overdue": "Hiển thị công việc quá hạn",
|
||||
"fromuntil": "Công việc từ {from} cho đến {until}",
|
||||
"select": "Chọn khoảng ngày",
|
||||
"noTasks": "Hôm nay thảnh thơi — Hãy tận hưởng ngày tuyệt vời!"
|
||||
},
|
||||
"detail": {
|
||||
|
@ -554,7 +603,7 @@
|
|||
"chooseStartDate": "Bấm vào đây để đặt ngày bắt đầu",
|
||||
"chooseEndDate": "Bấm vào đây để đặt ngày kết thúc",
|
||||
"move": "Di chuyển công việc sang một danh sách khác",
|
||||
"done": "Mark task done!",
|
||||
"done": "Đánh dấu đã xong!",
|
||||
"undone": "Đánh dấu Chưa xong",
|
||||
"created": "Đã tạo được {0} bởi {1}",
|
||||
"updated": "Đã cập nhật {0}",
|
||||
|
@ -570,22 +619,22 @@
|
|||
"text2": "Thao tác này cũng sẽ xóa tất cả tệp đính kèm, lời nhắc và liên kết đến công việc này. Nó không thể hoàn tác!"
|
||||
},
|
||||
"actions": {
|
||||
"assign": "Chỉ định một người",
|
||||
"assign": "Chỉ định người",
|
||||
"label": "Thêm nhãn",
|
||||
"priority": "Mức độ ưu tiên",
|
||||
"dueDate": "Đặt ngày đến hạn",
|
||||
"startDate": "Chọn ngày bắt đầu",
|
||||
"endDate": "Chọn ngày kết thúc",
|
||||
"reminders": "Thiết lập nhắc nhở",
|
||||
"repeatAfter": "Đặt khoảng lặp lại",
|
||||
"percentDone": "Chọn tỉ lệ hoàn thành",
|
||||
"attachments": "Đính kèm tệp",
|
||||
"relatedTasks": "Thêm liên kết công việc",
|
||||
"moveList": "Di chuyển công việc",
|
||||
"color": "Chọn màu",
|
||||
"delete": "Loại bỏ công việc",
|
||||
"favorite": "Lưu thành ưa thích",
|
||||
"unfavorite": "Gỡ khỏi ưa thích"
|
||||
"repeatAfter": "Chọn chu kì lặp",
|
||||
"percentDone": "Đặt tiến trình",
|
||||
"attachments": "Thêm Tệp đính kèm",
|
||||
"relatedTasks": "Thêm Quan hệ",
|
||||
"moveList": "Di chuyển",
|
||||
"color": "Đặt màu",
|
||||
"delete": "Xóa",
|
||||
"favorite": "Thêm vào mục yêu thích",
|
||||
"unfavorite": "Xóa khỏi mục yêu thích"
|
||||
}
|
||||
},
|
||||
"attributes": {
|
||||
|
@ -598,7 +647,7 @@
|
|||
"dueDate": "Ngày hết hạn",
|
||||
"endDate": "Ngày kết thúc",
|
||||
"labels": "Nhãn",
|
||||
"percentDone": "Tiến độ hoàn thành",
|
||||
"percentDone": "Tiến độ",
|
||||
"priority": "Mức độ ưu tiên",
|
||||
"relatedTasks": "Công việc liên quan",
|
||||
"reminders": "Nhắc nhở",
|
||||
|
@ -785,17 +834,20 @@
|
|||
"general": "Tổng quan",
|
||||
"allPages": "Các phím tắt này hoạt động ở mọi nơi.",
|
||||
"currentPageOnly": "Các phím tắt này chỉ hoạt động ở trang hiện tại.",
|
||||
"somePagesOnly": "Các phím tắt này chỉ hoạt động trên một số trang.",
|
||||
"toggleMenu": "Bật/tắt Menu",
|
||||
"quickSearch": "Mở thanh tìm kiếm/thao tác nhanh",
|
||||
"then": "sau đó",
|
||||
"task": {
|
||||
"title": "Trang công việc",
|
||||
"done": "Done",
|
||||
"assign": "Chỉ định một người",
|
||||
"done": "Đánh dấu công việc hoàn thành / chưa xong",
|
||||
"assign": "Chỉ định công việc cho một người",
|
||||
"labels": "Thêm nhãn cho công việc này",
|
||||
"dueDate": "Thay đổi ngày hết hạn của công việc này",
|
||||
"attachment": "Thêm tệp đính kèm cho công việc này",
|
||||
"related": "Sửa đổi các công việc liên kết"
|
||||
"related": "Sửa đổi các công việc liên kết",
|
||||
"color": "Thay đổi màu công việc này",
|
||||
"move": "Dời công việc này sang danh sách khác"
|
||||
},
|
||||
"list": {
|
||||
"title": "Xem danh sách",
|
||||
|
@ -803,6 +855,14 @@
|
|||
"switchToGanttView": "Chuyển sang biểu đồ Gantt",
|
||||
"switchToKanbanView": "Chuyển sang Kanban",
|
||||
"switchToTableView": "Chuyển qua xem Bảng"
|
||||
},
|
||||
"navigation": {
|
||||
"title": "Điều hướng",
|
||||
"overview": "Điều hướng đến trang tổng quan",
|
||||
"upcoming": "Điều hướng đến công việc sắp tới",
|
||||
"namespaces": "Điều hướng đến Không gian làm việc & danh sách",
|
||||
"labels": "Điều hướng đến nhãn",
|
||||
"teams": "Điều hướng đến Team"
|
||||
}
|
||||
},
|
||||
"update": {
|
||||
|
@ -908,7 +968,7 @@
|
|||
"4015": "Bình luận không tồn tại.",
|
||||
"4016": "Trường công việc không hợp lệ.",
|
||||
"4017": "Bộ so sánh bộ lọc công việc không hợp lệ.",
|
||||
"4018": "Invalid task filter concatenator.",
|
||||
"4018": "Bộ lọc kết hợp không hợp lệ.",
|
||||
"4019": "Giá trị bộ lọc công việc không hợp lệ.",
|
||||
"5001": "Góc làm việc không có nữa.",
|
||||
"5003": "Bạn chưa được phép bước vào vào góc làm việc được chỉ định.",
|
||||
|
|
|
@ -35,7 +35,6 @@ export function error(e, actions = []) {
|
|||
text: getErrorText(e),
|
||||
actions: actions,
|
||||
})
|
||||
console.error(e, actions)
|
||||
}
|
||||
|
||||
export function success(e, actions = []) {
|
||||
|
|
|
@ -7,6 +7,7 @@ export default class BackgroundImageModel extends AbstractModel {
|
|||
url: '',
|
||||
thumb: '',
|
||||
info: {},
|
||||
blurHash: '',
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@ export default class EmailUpdateModel extends AbstractModel {
|
|||
defaults() {
|
||||
return {
|
||||
newEmail: '',
|
||||
passwort: '',
|
||||
password: '',
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,7 +20,7 @@ export default class ListModel extends AbstractModel {
|
|||
|
||||
this.owner = new UserModel(this.owner)
|
||||
|
||||
if(typeof this.subscription !== 'undefined' && this.subscription !== null) {
|
||||
if (typeof this.subscription !== 'undefined' && this.subscription !== null) {
|
||||
this.subscription = new SubscriptionModel(this.subscription)
|
||||
}
|
||||
|
||||
|
@ -44,6 +44,7 @@ export default class ListModel extends AbstractModel {
|
|||
isFavorite: false,
|
||||
subscription: null,
|
||||
position: 0,
|
||||
backgroundBlurHash: '',
|
||||
|
||||
created: null,
|
||||
updated: null,
|
||||
|
|
|
@ -3,6 +3,8 @@ import {saveLastVisited} from '@/helpers/saveLastVisited'
|
|||
import {store} from '@/store'
|
||||
|
||||
import {saveListView, getListView} from '@/helpers/saveListView'
|
||||
import {parseDateOrString} from '@/helpers/time/parseDateOrString'
|
||||
import {getNextWeekDate} from '@/helpers/time/getNextWeekDate'
|
||||
|
||||
import HomeComponent from '../views/Home.vue'
|
||||
import NotFoundComponent from '../views/404.vue'
|
||||
|
@ -13,7 +15,7 @@ import RegisterComponent from '../views/user/Register.vue'
|
|||
import OpenIdAuth from '../views/user/OpenIdAuth.vue'
|
||||
import DataExportDownload from '../views/user/DataExportDownload.vue'
|
||||
// Tasks
|
||||
import ShowTasksInRangeComponent from '../views/tasks/ShowTasksInRange.vue'
|
||||
import UpcomingTasksComponent from '../views/tasks/ShowTasks.vue'
|
||||
import LinkShareAuthComponent from '../views/sharing/LinkSharingAuth.vue'
|
||||
import ListNamespaces from '../views/namespaces/ListNamespaces.vue'
|
||||
import TaskDetailView from '../views/tasks/TaskDetailView.vue'
|
||||
|
@ -248,7 +250,13 @@ const router = createRouter({
|
|||
{
|
||||
path: '/tasks/by/upcoming',
|
||||
name: 'tasks.range',
|
||||
component: ShowTasksInRangeComponent,
|
||||
component: UpcomingTasksComponent,
|
||||
props: route => ({
|
||||
dateFrom: parseDateOrString(route.query.from as string, new Date()),
|
||||
dateTo: parseDateOrString(route.query.to as string, getNextWeekDate()),
|
||||
showNulls: route.query.showNulls === 'true',
|
||||
showOverdue: route.query.showOverdue === 'true',
|
||||
}),
|
||||
},
|
||||
{
|
||||
path: '/lists/new/:namespaceId/',
|
||||
|
|
|
@ -2,6 +2,31 @@ import axios from 'axios'
|
|||
import {objectToSnakeCase} from '@/helpers/case'
|
||||
import {getToken} from '@/helpers/auth'
|
||||
|
||||
function convertObject(o) {
|
||||
if (o instanceof Date) {
|
||||
return o.toISOString()
|
||||
}
|
||||
|
||||
return o
|
||||
}
|
||||
|
||||
function prepareParams(params) {
|
||||
if (typeof params !== 'object') {
|
||||
return params
|
||||
}
|
||||
|
||||
for (const p in params) {
|
||||
if (Array.isArray(params[p])) {
|
||||
params[p] = params[p].map(convertObject)
|
||||
continue
|
||||
}
|
||||
|
||||
params[p] = convertObject(params[p])
|
||||
}
|
||||
|
||||
return objectToSnakeCase(params)
|
||||
}
|
||||
|
||||
export default class AbstractService {
|
||||
|
||||
/////////////////////////////
|
||||
|
@ -292,7 +317,7 @@ export default class AbstractService {
|
|||
const finalUrl = this.getReplacedRoute(url, model)
|
||||
|
||||
try {
|
||||
const response = await this.http.get(finalUrl, {params})
|
||||
const response = await this.http.get(finalUrl, {params: prepareParams(params)})
|
||||
const result = this.modelGetFactory(response.data)
|
||||
result.maxRight = Number(response.headers['x-max-right'])
|
||||
return result
|
||||
|
@ -331,7 +356,7 @@ export default class AbstractService {
|
|||
const finalUrl = this.getReplacedRoute(this.paths.getAll, model)
|
||||
|
||||
try {
|
||||
const response = await this.http.get(finalUrl, {params: params})
|
||||
const response = await this.http.get(finalUrl, {params: prepareParams(params)})
|
||||
this.resultCount = Number(response.headers['x-pagination-result-count'])
|
||||
this.totalPages = Number(response.headers['x-pagination-total-pages'])
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import {createStore} from 'vuex'
|
||||
import {getBlobFromBlurHash} from '../helpers/getBlobFromBlurHash'
|
||||
import {
|
||||
BACKGROUND,
|
||||
BLUR_HASH,
|
||||
CURRENT_LIST,
|
||||
HAS_TASKS,
|
||||
KEYBOARD_SHORTCUTS_ACTIVE,
|
||||
|
@ -44,10 +46,12 @@ export const store = createStore({
|
|||
isArchived: false,
|
||||
}),
|
||||
background: '',
|
||||
blurHash: '',
|
||||
hasTasks: false,
|
||||
menuActive: true,
|
||||
keyboardShortcutsActive: false,
|
||||
quickActionsActive: false,
|
||||
modalActive: false,
|
||||
},
|
||||
mutations: {
|
||||
[LOADING](state, loading) {
|
||||
|
@ -83,6 +87,12 @@ export const store = createStore({
|
|||
[BACKGROUND](state, background) {
|
||||
state.background = background
|
||||
},
|
||||
[BLUR_HASH](state, blurHash) {
|
||||
state.blurHash = blurHash
|
||||
},
|
||||
modalActive(state, active) {
|
||||
state.modalActive = active
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
async [CURRENT_LIST]({state, commit}, currentList) {
|
||||
|
@ -90,6 +100,7 @@ export const store = createStore({
|
|||
if (currentList === null) {
|
||||
commit(CURRENT_LIST, {})
|
||||
commit(BACKGROUND, null)
|
||||
commit(BLUR_HASH, null)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -122,10 +133,15 @@ export const store = createStore({
|
|||
) {
|
||||
if (currentList.backgroundInformation) {
|
||||
try {
|
||||
const blurHash = await getBlobFromBlurHash(currentList.backgroundBlurHash)
|
||||
if (blurHash) {
|
||||
commit(BLUR_HASH, window.URL.createObjectURL(blurHash))
|
||||
}
|
||||
|
||||
const listService = new ListService()
|
||||
const background = await listService.background(currentList)
|
||||
commit(BACKGROUND, background)
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
console.error('Error getting background image for list', currentList.id, e)
|
||||
}
|
||||
}
|
||||
|
@ -133,6 +149,7 @@ export const store = createStore({
|
|||
|
||||
if (typeof currentList.backgroundInformation === 'undefined' || currentList.backgroundInformation === null) {
|
||||
commit(BACKGROUND, null)
|
||||
commit(BLUR_HASH, null)
|
||||
}
|
||||
|
||||
commit(CURRENT_LIST, currentList)
|
||||
|
|
|
@ -133,9 +133,19 @@ export default {
|
|||
console.debug('Could not add assignee to task in kanban, task not found', t)
|
||||
return r
|
||||
}
|
||||
// FIXME: direct store manipulation (task)
|
||||
t.task.assignees.push(user)
|
||||
ctx.commit('kanban/setTaskInBucketByIndex', t, { root: true })
|
||||
|
||||
const assignees = [
|
||||
...t.task.assignees,
|
||||
user,
|
||||
]
|
||||
|
||||
ctx.commit('kanban/setTaskInBucketByIndex', {
|
||||
...t,
|
||||
task: {
|
||||
...t.task,
|
||||
assignees,
|
||||
},
|
||||
}, {root: true})
|
||||
return r
|
||||
},
|
||||
|
||||
|
@ -153,15 +163,15 @@ export default {
|
|||
return response
|
||||
}
|
||||
|
||||
for (const a in t.task.assignees) {
|
||||
if (t.task.assignees[a].id === user.id) {
|
||||
// FIXME: direct store manipulation (task)
|
||||
t.task.assignees.splice(a, 1)
|
||||
break
|
||||
}
|
||||
}
|
||||
const assignees = t.task.assignees.filter(({ id }) => id !== user.id)
|
||||
|
||||
ctx.commit('kanban/setTaskInBucketByIndex', t, {root: true})
|
||||
ctx.commit('kanban/setTaskInBucketByIndex', {
|
||||
...t,
|
||||
task: {
|
||||
...t.task,
|
||||
assignees,
|
||||
},
|
||||
}, {root: true})
|
||||
return response
|
||||
|
||||
},
|
||||
|
@ -179,17 +189,19 @@ export default {
|
|||
console.debug('Could not add label to task in kanban, task not found', t)
|
||||
return r
|
||||
}
|
||||
|
||||
const labels = [...t.task.labels]
|
||||
labels.push(label)
|
||||
|
||||
const labels = [
|
||||
...t.task.labels,
|
||||
label,
|
||||
]
|
||||
|
||||
ctx.commit('kanban/setTaskInBucketByIndex', {
|
||||
task: {
|
||||
labels,
|
||||
...t.task,
|
||||
},
|
||||
...t,
|
||||
}, { root: true })
|
||||
task: {
|
||||
...t.task,
|
||||
labels,
|
||||
},
|
||||
}, {root: true})
|
||||
|
||||
return r
|
||||
},
|
||||
|
@ -209,20 +221,14 @@ export default {
|
|||
}
|
||||
|
||||
// Remove the label from the list
|
||||
const labels = [...t.task.labels]
|
||||
for (const l in labels) {
|
||||
if (labels[l].id === label.id) {
|
||||
labels.splice(l, 1)
|
||||
break
|
||||
}
|
||||
}
|
||||
const labels = t.task.labels.filter(({ id }) => id !== label.id)
|
||||
|
||||
ctx.commit('kanban/setTaskInBucketByIndex', {
|
||||
task: {
|
||||
labels,
|
||||
...t.task,
|
||||
},
|
||||
...t,
|
||||
task: {
|
||||
...t.task,
|
||||
labels,
|
||||
},
|
||||
}, {root: true})
|
||||
|
||||
return response
|
||||
|
|
|
@ -6,6 +6,6 @@ export const MENU_ACTIVE = 'menuActive'
|
|||
export const KEYBOARD_SHORTCUTS_ACTIVE = 'keyboardShortcutsActive'
|
||||
export const QUICK_ACTIONS_ACTIVE = 'quickActionsActive'
|
||||
export const BACKGROUND = 'background'
|
||||
export const BLUR_HASH = 'blurHash'
|
||||
|
||||
export const CONFIG = 'config'
|
||||
export const AUTH = 'auth'
|
||||
|
|
|
@ -23,7 +23,7 @@ $filter-container-top-link-share-list: -47px;
|
|||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
.button:not(:last-child) {
|
||||
.button:not(:last-of-type) {
|
||||
margin-right: .5rem;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,182 @@
|
|||
// Vikunja colors as CSS custom properties
|
||||
|
||||
:root {
|
||||
// Core Bulma color variables
|
||||
// Added (from bulma-css-variables/css/bulma.css) to fix issues with undefined variables
|
||||
// This section should be removed once bulma/sass scoping issues are fixed
|
||||
// see https://kolaente.dev/vikunja/frontend/issues/1064
|
||||
// Variables overriden in Vikunja specific rules below are commented out
|
||||
//--scheme-main: white;
|
||||
--scheme-main-bis: #fafafa;
|
||||
--scheme-main-ter: whitesmoke;
|
||||
--scheme-invert: #0a0a0a;
|
||||
--scheme-invert-rgb: 10.2, 10.2, 10.2;
|
||||
--scheme-invert-bis: #121212;
|
||||
--scheme-invert-ter: #242424;
|
||||
--background: whitesmoke;
|
||||
//--border: #dbdbdb;
|
||||
--border-rgb: 219.3, 219.3, 219.3;
|
||||
--border-hover: #b5b5b5;
|
||||
--border-light: #ededed;
|
||||
--border-light-hover: #b5b5b5;
|
||||
--text: #4a4a4a;
|
||||
--text-invert: #fff;
|
||||
--text-light: #7a7a7a;
|
||||
--text-strong: #363636;
|
||||
--code: #da1039;
|
||||
--code-background: var(--background, whitesmoke);
|
||||
--pre: var(--text, #4a4a4a);
|
||||
--pre-background: var(--background, whitesmoke);
|
||||
--link-visited: #b86bff;
|
||||
//--link-hover: #363636;
|
||||
--link-hover-border: #b5b5b5;
|
||||
--link-focus: #363636;
|
||||
--link-focus-border: var(--link, #485fc7);
|
||||
--link-active: #363636;
|
||||
--link-active-border: #4a4a4a;
|
||||
//--white-h: 0deg;
|
||||
//--white-s: 0%;
|
||||
//--white-l: 100%;
|
||||
//--white-a: 1;
|
||||
//--white: hsla(var(--white-h), var(--white-s), var(--white-l), var(--white-a));
|
||||
--white-invert-l: 4%;
|
||||
--white-invert: #0a0a0a;
|
||||
--white-light-l: 100%;
|
||||
--white-light: hsla(var(--white-h), var(--white-s), var(--white-light-l), var(--white-a));
|
||||
--white-dark-l: 29%;
|
||||
--white-dark: hsla(var(--white-h), var(--white-s), var(--white-dark-l), var(--white-a));
|
||||
//--black-h: 0deg;
|
||||
//--black-s: 0%;
|
||||
//--black-l: 4%;
|
||||
//--black-a: 1;
|
||||
//--black: hsla(var(--black-h), var(--black-s), var(--black-l), var(--black-a));
|
||||
--black-invert-l: 100%;
|
||||
--black-invert: white;
|
||||
--black-light-l: 96%;
|
||||
--black-light: hsla(var(--black-h), var(--black-s), var(--black-light-l), var(--black-a));
|
||||
--black-dark-l: 57%;
|
||||
--black-dark: hsla(var(--black-h), var(--black-s), var(--black-dark-l), var(--black-a));
|
||||
--light-h: 0deg;
|
||||
--light-s: 0%;
|
||||
--light-l: 96%;
|
||||
--light-a: 1;
|
||||
--light: hsla(var(--light-h), var(--light-s), var(--light-l), var(--light-a));
|
||||
--light-invert-l: 0%;
|
||||
--light-invert: rgba(0, 0, 0, 0.7);
|
||||
--light-light-l: 96%;
|
||||
--light-light: hsla(var(--light-h), var(--light-s), var(--light-light-l), var(--light-a));
|
||||
--light-dark-l: 29%;
|
||||
--light-dark: hsla(var(--light-h), var(--light-s), var(--light-dark-l), var(--light-a));
|
||||
--dark-h: 0deg;
|
||||
--dark-s: 0%;
|
||||
--dark-l: 21%;
|
||||
--dark-a: 1;
|
||||
--dark: hsla(var(--dark-h), var(--dark-s), var(--dark-l), var(--dark-a));
|
||||
--dark-invert-l: 100%;
|
||||
--dark-invert: #fff;
|
||||
--dark-light-l: 96%;
|
||||
--dark-light: hsla(var(--dark-h), var(--dark-s), var(--dark-light-l), var(--dark-a));
|
||||
--dark-dark-l: 54%;
|
||||
--dark-dark: hsla(var(--dark-h), var(--dark-s), var(--dark-dark-l), var(--dark-a));
|
||||
//--primary-h: 171deg;
|
||||
//--primary-s: 100%;
|
||||
//--primary-l: 41%;
|
||||
//--primary-a: 1;
|
||||
//--primary: hsla(var(--primary-h), var(--primary-s), var(--primary-l), var(--primary-a));
|
||||
--primary-invert-l: 100%;
|
||||
--primary-invert: #fff;
|
||||
--primary-light-l: 96%;
|
||||
--primary-light: hsla(var(--primary-h), var(--primary-s), var(--primary-light-l), var(--primary-a));
|
||||
--primary-dark-l: 29%;
|
||||
--primary-dark: hsla(var(--primary-h), var(--primary-s), var(--primary-dark-l), var(--primary-a));
|
||||
--link-h: 229deg;
|
||||
--link-s: 53%;
|
||||
--link-l: 53%;
|
||||
--link-a: 1;
|
||||
//--link: hsla(var(--link-h), var(--link-s), var(--link-l), var(--link-a));
|
||||
--link-invert-l: 100%;
|
||||
--link-invert: #fff;
|
||||
--link-light-l: 96%;
|
||||
--link-light: hsla(var(--link-h), var(--link-s), var(--link-light-l), var(--link-a));
|
||||
--link-dark-l: 47%;
|
||||
--link-dark: hsla(var(--link-h), var(--link-s), var(--link-dark-l), var(--link-a));
|
||||
--info-h: 207deg;
|
||||
--info-s: 61%;
|
||||
--info-l: 53%;
|
||||
--info-a: 1;
|
||||
--info: hsla(var(--info-h), var(--info-s), var(--info-l), var(--info-a));
|
||||
--info-invert-l: 100%;
|
||||
--info-invert: #fff;
|
||||
--info-light-l: 96%;
|
||||
--info-light: hsla(var(--info-h), var(--info-s), var(--info-light-l), var(--info-a));
|
||||
--info-dark-l: 41%;
|
||||
--info-dark: hsla(var(--info-h), var(--info-s), var(--info-dark-l), var(--info-a));
|
||||
//--success-h: 153deg;
|
||||
//--success-s: 53%;
|
||||
//--success-l: 53%;
|
||||
//--success-a: 1;
|
||||
//--success: hsla(var(--success-h), var(--success-s), var(--success-l), var(--success-a));
|
||||
--success-invert-l: 100%;
|
||||
--success-invert: #fff;
|
||||
--success-light-l: 96%;
|
||||
--success-light: hsla(var(--success-h), var(--success-s), var(--success-light-l), var(--success-a));
|
||||
--success-dark-l: 31%;
|
||||
--success-dark: hsla(var(--success-h), var(--success-s), var(--success-dark-l), var(--success-a));
|
||||
//--warning-h: 44deg;
|
||||
//--warning-s: 100%;
|
||||
//--warning-l: 77%;
|
||||
//--warning-a: 1;
|
||||
//--warning: hsla(var(--warning-h), var(--warning-s), var(--warning-l), var(--warning-a));
|
||||
--warning-invert-l: 0%;
|
||||
--warning-invert: rgba(0, 0, 0, 0.7);
|
||||
--warning-light-l: 96%;
|
||||
--warning-light: hsla(var(--warning-h), var(--warning-s), var(--warning-light-l), var(--warning-a));
|
||||
--warning-dark-l: 29%;
|
||||
--warning-dark: hsla(var(--warning-h), var(--warning-s), var(--warning-dark-l), var(--warning-a));
|
||||
//--danger-h: 348deg;
|
||||
//--danger-s: 86%;
|
||||
//--danger-l: 61%;
|
||||
//--danger-a: 1;
|
||||
//--danger: hsla(var(--danger-h), var(--danger-s), var(--danger-l), var(--danger-a));
|
||||
--danger-invert-l: 100%;
|
||||
--danger-invert: #fff;
|
||||
--danger-light-l: 96%;
|
||||
--danger-light: hsla(var(--danger-h), var(--danger-s), var(--danger-light-l), var(--danger-a));
|
||||
--danger-dark-l: 43%;
|
||||
--danger-dark: hsla(var(--danger-h), var(--danger-s), var(--danger-dark-l), var(--danger-a));
|
||||
--input-color: var(--text-strong, #363636);
|
||||
--input-background-color: var(--scheme-main, white);
|
||||
//--input-border-color: var(--border, #dbdbdb);
|
||||
--input-shadow: inset 0 0.0625em 0.125em rgba(var(--scheme-invert-rgb, 10.2, 10.2, 10.2), 0.05);
|
||||
--input-placeholder-color: rgba(54, 54, 54, 0.3);
|
||||
--input-hover-color: var(--text-strong, #363636);
|
||||
--input-hover-border-color: var(--border-hover, #b5b5b5);
|
||||
--input-focus-color: var(--text-strong, #363636);
|
||||
--input-focus-border-color: var(--link, #485fc7);
|
||||
--input-focus-box-shadow-color: var(--input-focus-box-shadow-color-hsla, rgba(72, 95, 199, 0.25));
|
||||
--input-disabled-color: var(--text-light, #7a7a7a);
|
||||
//--input-disabled-background-color: var(--background, whitesmoke);
|
||||
//--input-disabled-border-color: var(--background, whitesmoke);
|
||||
--input-disabled-placeholder-color: rgba(122, 122, 122, 0.3);
|
||||
--input-arrow: var(--link, #485fc7);
|
||||
--input-icon-color: var(--border, #dbdbdb);
|
||||
--input-icon-active-color: var(--text, #4a4a4a);
|
||||
--black-bis: #121212;
|
||||
--black-ter: #242424;
|
||||
//--grey-darker: #363636;
|
||||
//--grey-dark: #4a4a4a;
|
||||
//--grey: #7a7a7a;
|
||||
//--grey-light: #b5b5b5;
|
||||
//--grey-lighter: #dbdbdb;
|
||||
--white-ter: whitesmoke;
|
||||
--white-bis: #fafafa;
|
||||
// END core Bulma color variables
|
||||
|
||||
|
||||
// Vikunja specific variables
|
||||
--grey-50: hsl(210, 20%, 98%);
|
||||
--grey-100: hsl(220, 14.3%, 95.9%);
|
||||
--grey-100-hsl: 220, 14.3%, 95.9%;
|
||||
--grey-100: hsl(var(--grey-100-hsl));
|
||||
--grey-200: hsl(220, 13%, 91%);
|
||||
--grey-300: hsl(216, 12.2%, 83.9%);
|
||||
--grey-400: hsl(217.9, 10.6%, 64.9%);
|
||||
|
@ -14,9 +187,9 @@
|
|||
--grey-800: hsl(215, 27.9%, 16.9%);
|
||||
--grey-900: hsl(220.9, 39.3%, 11%);
|
||||
--site-background: var(--grey-100);
|
||||
--scheme-main: var(--white);
|
||||
|
||||
// Overrides of Bulma defaults
|
||||
--scheme-main: var(--white);
|
||||
--grey-darker: var(--grey-700);
|
||||
--grey-dark: var(--grey-800);
|
||||
--grey: var(--grey-500);
|
||||
|
@ -66,6 +239,12 @@
|
|||
--primary-a: 1;
|
||||
--primary-hsl: var(--primary-h), var(--primary-s), var(--primary-l);
|
||||
--primary: hsla(var(--primary-h), var(--primary-s), var(--primary-l), var(--primary-a));
|
||||
|
||||
--link: var(--primary);
|
||||
--link-hover: hsla(var(--primary-h), var(--primary-s), var(--primary-l), .75);
|
||||
--border: var(--grey-200);
|
||||
--input-disabled-background-color: var(--grey-100);
|
||||
--input-disabled-border-color: var(--grey-300);
|
||||
// END Overrides of Bulma defaults
|
||||
|
||||
|
||||
|
@ -76,12 +255,6 @@
|
|||
--card-border-color: var(--grey-200);
|
||||
--logo-text-color: hsl(180, 1%, 15%);
|
||||
|
||||
--link: var(--primary);
|
||||
--link-hover: hsla(var(--primary-h), var(--primary-s), var(--primary-l), .75);
|
||||
--border: var(--grey-200);
|
||||
--input-disabled-background-color: var(--grey-100);
|
||||
--input-disabled-border-color: var(--grey-300);
|
||||
|
||||
&.dark {
|
||||
// Light mode colours reversed for dark mode
|
||||
--grey-900-hsl: 210, 20%, 98%;
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
.app-container.has-background,
|
||||
.link-share-container.has-background {
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
background-attachment: fixed;
|
||||
min-height: 100vh;
|
||||
position: relative;
|
||||
|
||||
// FIXME: move to pagination component
|
||||
&, .app-container-background {
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
background-attachment: fixed;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
// FIXME: move to pagination component
|
||||
.pagination-link:not(.is-current) {
|
||||
background: var(--grey-100);
|
||||
}
|
||||
|
@ -32,4 +36,21 @@
|
|||
border-radius: $radius !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.app-container-background {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
position: fixed;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.background-fade-in {
|
||||
opacity: 0;
|
||||
transition: opacity $transition;
|
||||
transition-delay: $transition-duration * 2; // To fake an appearing background
|
||||
|
||||
&.is-visible {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,11 @@
|
|||
box-shadow: 0 0 0 2px hsla(var(--primary-hsl), 0.5);
|
||||
}
|
||||
|
||||
:root {
|
||||
// Bulma sets this to "scroll" which gives us a scrollbar even if there's no content to scroll
|
||||
--body-overflow-y: auto;
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--site-background);
|
||||
min-height: 100vh;
|
||||
|
@ -65,7 +70,7 @@ h6 {
|
|||
}
|
||||
|
||||
.has-overflow {
|
||||
overflow: visible;
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
||||
.has-horizontal-overflow {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* eslint-disable no-console */
|
||||
/* eslint-disable no-undef */
|
||||
|
||||
const workboxVersion = 'v6.4.2'
|
||||
const workboxVersion = 'v6.5.2'
|
||||
importScripts( `/workbox-${workboxVersion}/workbox-sw.js`)
|
||||
workbox.setConfig({
|
||||
modulePathPrefix: `/workbox-${workboxVersion}`,
|
||||
|
|
|
@ -50,7 +50,11 @@
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
<ShowTasks class="mt-4" :show-all="true" v-if="hasLists" :key="showTasksKey"/>
|
||||
<ShowTasks
|
||||
v-if="hasLists"
|
||||
class="mt-4"
|
||||
:key="showTasksKey"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -83,13 +87,14 @@ const userInfo = computed(() => store.state.auth.info)
|
|||
const hasTasks = computed(() => store.state.hasTasks)
|
||||
const defaultListId = computed(() => store.state.auth.defaultListId)
|
||||
const defaultNamespaceId = computed(() => store.state.namespaces.namespaces?.[0]?.id || 0)
|
||||
const hasLists = computed (() => store.state.namespaces.namespaces?.[0]?.lists.length > 0)
|
||||
const hasLists = computed(() => store.state.namespaces.namespaces?.[0]?.lists.length > 0)
|
||||
const loading = computed(() => store.state.loading && store.state.loadingModule === 'tasks')
|
||||
const deletionScheduledAt = computed(() => parseDateOrNull(store.state.auth.info?.deletionScheduledAt))
|
||||
|
||||
// This is to reload the tasks list after adding a new task through the global task add.
|
||||
// FIXME: Should use vuex (somehow?)
|
||||
const showTasksKey = ref(0)
|
||||
|
||||
function updateTaskList() {
|
||||
showTasksKey.value++
|
||||
}
|
||||
|
|
|
@ -616,7 +616,7 @@ $crazy-height-calculation-tasks: '#{$crazy-height-calculation} - 1rem - 2.5rem -
|
|||
$filter-container-height: '1rem - #{$switch-view-height}';
|
||||
|
||||
// FIXME:
|
||||
.app-content.list\.kanban {
|
||||
.app-content.list\.kanban, .app-content.task\.detail {
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
|
||||
|
@ -659,11 +659,12 @@ $filter-container-height: '1rem - #{$switch-view-height}';
|
|||
position: relative;
|
||||
|
||||
margin: 0 $bucket-right-margin 0 0;
|
||||
max-height: 100%;
|
||||
max-height: calc(100% - 1rem); // 1rem spacing to the bottom
|
||||
min-height: 20px;
|
||||
width: $bucket-width;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden; // Make sure the edges are always rounded
|
||||
|
||||
.tasks {
|
||||
overflow: hidden auto;
|
||||
|
@ -764,6 +765,7 @@ $filter-container-height: '1rem - #{$switch-view-height}';
|
|||
background-color: var(--grey-100);
|
||||
border-bottom-left-radius: $radius;
|
||||
border-bottom-right-radius: $radius;
|
||||
transform: none;
|
||||
|
||||
.button {
|
||||
background-color: transparent;
|
||||
|
@ -775,6 +777,7 @@ $filter-container-height: '1rem - #{$switch-view-height}';
|
|||
}
|
||||
}
|
||||
|
||||
// FIXME: This does not seem to work
|
||||
.task-dragging {
|
||||
transform: rotateZ(3deg);
|
||||
transition: transform 0.18s ease;
|
||||
|
|
|
@ -299,6 +299,7 @@ export default defineComponent({
|
|||
break
|
||||
}
|
||||
}
|
||||
// FIXME: Use computed
|
||||
sortTasks(this.tasks)
|
||||
},
|
||||
|
||||
|
|
|
@ -42,12 +42,12 @@
|
|||
</Message>
|
||||
</transition>
|
||||
|
||||
<slot />
|
||||
<slot v-if="loadedListId"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {ref, shallowRef, computed, watchEffect} from 'vue'
|
||||
import {ref, computed, watch, watchEffect} from 'vue'
|
||||
import {useRoute} from 'vue-router'
|
||||
|
||||
import Message from '@/components/misc/message.vue'
|
||||
|
@ -55,12 +55,12 @@ import Message from '@/components/misc/message.vue'
|
|||
import ListModel from '@/models/list'
|
||||
import ListService from '@/services/list'
|
||||
|
||||
import {store} from '@/store'
|
||||
import {CURRENT_LIST} from '@/store/mutation-types'
|
||||
import {BACKGROUND, BLUR_HASH, CURRENT_LIST} from '@/store/mutation-types'
|
||||
|
||||
import {getListTitle} from '@/helpers/getListTitle'
|
||||
import {saveListToHistory} from '@/modules/listHistory'
|
||||
import { useTitle } from '@/composables/useTitle'
|
||||
import {useTitle} from '@/composables/useTitle'
|
||||
import {useStore} from 'vuex'
|
||||
|
||||
const props = defineProps({
|
||||
listId: {
|
||||
|
@ -74,8 +74,9 @@ const props = defineProps({
|
|||
})
|
||||
|
||||
const route = useRoute()
|
||||
const store = useStore()
|
||||
|
||||
const listService = shallowRef(new ListService())
|
||||
const listService = ref(new ListService())
|
||||
const loadedListId = ref(0)
|
||||
|
||||
const currentList = computed(() => {
|
||||
|
@ -87,7 +88,22 @@ const currentList = computed(() => {
|
|||
} : store.state.currentList
|
||||
})
|
||||
|
||||
// call again the method if the listId changes
|
||||
// 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 list multiple times, even when navigating away from it.
|
||||
// This caused wired bugs where the list background would be set on the home page but only right after setting a new
|
||||
// list background and then navigating to home. It also highlighted the list in the menu and didn't allow changing any
|
||||
// of it, most likely due to the rights not being properly populated.
|
||||
watch(
|
||||
() => props.listId,
|
||||
(listId) => {
|
||||
loadList(listId)
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
},
|
||||
)
|
||||
|
||||
// call the method again if the listId changes
|
||||
watchEffect(() => loadList(props.listId))
|
||||
|
||||
useTitle(() => currentList.value.id ? getListTitle(currentList.value) : '')
|
||||
|
@ -118,11 +134,21 @@ async function loadList(listIdToLoad: number) {
|
|||
)
|
||||
&& typeof currentList.value !== 'undefined' && currentList.value.maxRight !== null
|
||||
) {
|
||||
loadedListId.value = props.listId
|
||||
return
|
||||
}
|
||||
|
||||
console.debug(`Loading list, props.viewName = ${props.viewName}, $route.params =`, route.params, `, loadedListId = ${loadedListId.value}, currentList = `, currentList.value)
|
||||
|
||||
// Set the current list to the one we're about to load so that the title is already shown at the top
|
||||
loadedListId.value = 0
|
||||
const listFromStore = store.getters['lists/getListById'](listData.id)
|
||||
if (listFromStore !== null) {
|
||||
store.commit(BACKGROUND, null)
|
||||
store.commit(BLUR_HASH, null)
|
||||
store.commit(CURRENT_LIST, listFromStore)
|
||||
}
|
||||
|
||||
// We create an extra list object instead of creating it in list.value because that would trigger a ui update which would result in bad ux.
|
||||
const list = new ListModel(listData)
|
||||
try {
|
||||
|
@ -139,6 +165,7 @@ async function loadList(listIdToLoad: number) {
|
|||
@media screen and (max-width: $tablet) {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -149,7 +176,7 @@ async function loadList(listIdToLoad: number) {
|
|||
font-size: .75rem;
|
||||
box-shadow: var(--shadow-sm);
|
||||
height: $switch-view-height;
|
||||
margin-bottom: 1rem;
|
||||
margin: 0 auto 1rem;
|
||||
padding: .5rem;
|
||||
|
||||
a {
|
||||
|
|
|
@ -35,16 +35,25 @@
|
|||
v-model="backgroundSearchTerm"
|
||||
/>
|
||||
<p class="unsplash-link">
|
||||
<a href="https://unsplash.com" rel="noreferrer noopener nofollow" target="_blank">{{ $t('list.background.poweredByUnsplash') }}</a>
|
||||
<a href="https://unsplash.com" rel="noreferrer noopener nofollow" target="_blank">
|
||||
{{ $t('list.background.poweredByUnsplash') }}
|
||||
</a>
|
||||
</p>
|
||||
<div class="image-search-result">
|
||||
<a
|
||||
:key="im.id"
|
||||
:style="{'background-image': `url(${backgroundThumbs[im.id]})`}"
|
||||
:style="{'background-image': `url(${backgroundBlurHashes[im.id]})`}"
|
||||
@click="() => setBackground(im.id)"
|
||||
class="image"
|
||||
v-for="im in backgroundSearchResult">
|
||||
<a :href="`https://unsplash.com/@${im.info.author}`" rel="noreferrer noopener nofollow" target="_blank" class="info">
|
||||
<transition name="fade">
|
||||
<img :src="backgroundThumbs[im.id]" alt="" v-if="backgroundThumbs[im.id]"/>
|
||||
</transition>
|
||||
<a
|
||||
:href="`https://unsplash.com/@${im.info.author}`"
|
||||
rel="noreferrer noopener nofollow"
|
||||
target="_blank"
|
||||
class="info">
|
||||
{{ im.info.authorName }}
|
||||
</a>
|
||||
</a>
|
||||
|
@ -65,6 +74,8 @@
|
|||
|
||||
<script>
|
||||
import {mapState} from 'vuex'
|
||||
import {getBlobFromBlurHash} from '../../../helpers/getBlobFromBlurHash'
|
||||
|
||||
import BackgroundUnsplashService from '../../../services/backgroundUnsplash'
|
||||
import BackgroundUploadService from '../../../services/backgroundUpload'
|
||||
import ListService from '@/services/list'
|
||||
|
@ -83,6 +94,7 @@ export default {
|
|||
backgroundSearchTerm: '',
|
||||
backgroundSearchResult: [],
|
||||
backgroundThumbs: {},
|
||||
backgroundBlurHashes: {},
|
||||
currentPage: 1,
|
||||
|
||||
// We're using debounce to not search on every keypress but with a delay.
|
||||
|
@ -120,8 +132,16 @@ export default {
|
|||
this.currentPage = page
|
||||
const result = await this.backgroundService.getAll({}, {s: this.backgroundSearchTerm, p: page})
|
||||
this.backgroundSearchResult = this.backgroundSearchResult.concat(result)
|
||||
result.forEach(async background => {
|
||||
this.backgroundThumbs[background.id] = await this.backgroundService.thumb(background)
|
||||
result.forEach(background => {
|
||||
getBlobFromBlurHash(background.blurHash)
|
||||
.then(b => {
|
||||
this.backgroundBlurHashes[background.id] = window.URL.createObjectURL(b)
|
||||
})
|
||||
|
||||
this.backgroundService.thumb(background)
|
||||
.then(b => {
|
||||
this.backgroundThumbs[background.id] = b
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
|
@ -134,6 +154,7 @@ export default {
|
|||
const list = await this.backgroundService.update({id: backgroundId, listId: this.$route.params.listId})
|
||||
await this.$store.dispatch(CURRENT_LIST, list)
|
||||
this.$store.commit('namespaces/setListInNamespaceById', list)
|
||||
this.$store.commit('lists/setList', list)
|
||||
this.$message.success({message: this.$t('list.background.success')})
|
||||
},
|
||||
|
||||
|
@ -145,6 +166,7 @@ export default {
|
|||
const list = await this.backgroundUploadService.create(this.$route.params.listId, this.$refs.backgroundUploadInput.files[0])
|
||||
await this.$store.dispatch(CURRENT_LIST, list)
|
||||
this.$store.commit('namespaces/setListInNamespaceById', list)
|
||||
this.$store.commit('lists/setList', list)
|
||||
this.$message.success({message: this.$t('list.background.success')})
|
||||
},
|
||||
|
||||
|
@ -152,6 +174,7 @@ export default {
|
|||
const list = await this.listService.removeBackground(this.currentList)
|
||||
await this.$store.dispatch(CURRENT_LIST, list)
|
||||
this.$store.commit('namespaces/setListInNamespaceById', list)
|
||||
this.$store.commit('lists/setList', list)
|
||||
this.$message.success({message: this.$t('list.background.removeSuccess')})
|
||||
this.$router.back()
|
||||
},
|
||||
|
@ -162,82 +185,88 @@ export default {
|
|||
<style lang="scss" scoped>
|
||||
.list-background-setting {
|
||||
|
||||
.unsplash-link {
|
||||
text-align: right;
|
||||
font-size: .8rem;
|
||||
.unsplash-link {
|
||||
text-align: right;
|
||||
font-size: .8rem;
|
||||
|
||||
a {
|
||||
color: var(--grey-800);
|
||||
}
|
||||
}
|
||||
a {
|
||||
color: var(--grey-800);
|
||||
}
|
||||
}
|
||||
|
||||
.image-search-result {
|
||||
margin-top: 1rem;
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
.image-search-result {
|
||||
margin-top: 1rem;
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
|
||||
.image {
|
||||
width: calc(100% / 5 - 1rem);
|
||||
height: 120px;
|
||||
margin: .5rem;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
display: flex;
|
||||
.image {
|
||||
width: calc(100% / 5 - 1rem);
|
||||
height: 120px;
|
||||
margin: .5rem;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
display: flex;
|
||||
position: relative;
|
||||
|
||||
@media screen and (min-width: $desktop) {
|
||||
&:nth-child(5n) {
|
||||
break-after: always;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: $desktop) {
|
||||
&:nth-child(5n) {
|
||||
break-after: always;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: $desktop) {
|
||||
width: calc(100% / 4 - 1rem);
|
||||
@media screen and (max-width: $desktop) {
|
||||
width: calc(100% / 4 - 1rem);
|
||||
|
||||
&:nth-child(4n) {
|
||||
break-after: always;
|
||||
}
|
||||
}
|
||||
&:nth-child(4n) {
|
||||
break-after: always;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: $tablet) {
|
||||
width: calc(100% / 2 - 1rem);
|
||||
@media screen and (max-width: $tablet) {
|
||||
width: calc(100% / 2 - 1rem);
|
||||
|
||||
&:nth-child(2n) {
|
||||
break-after: always;
|
||||
}
|
||||
}
|
||||
&:nth-child(2n) {
|
||||
break-after: always;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: ($mobile)) {
|
||||
width: calc(100% - 1rem);
|
||||
@media screen and (max-width: ($mobile)) {
|
||||
width: calc(100% - 1rem);
|
||||
|
||||
&:nth-child(1n) {
|
||||
break-after: always;
|
||||
}
|
||||
}
|
||||
&:nth-child(1n) {
|
||||
break-after: always;
|
||||
}
|
||||
}
|
||||
|
||||
.info {
|
||||
align-self: flex-end;
|
||||
display: block;
|
||||
opacity: 0;
|
||||
width: 100%;
|
||||
padding: .25rem 0;
|
||||
text-align: center;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
font-size: .75rem;
|
||||
font-weight: bold;
|
||||
color: var(--white);
|
||||
transition: opacity $transition;
|
||||
}
|
||||
.info {
|
||||
align-self: flex-end;
|
||||
display: block;
|
||||
opacity: 0;
|
||||
width: 100%;
|
||||
padding: .25rem 0;
|
||||
text-align: center;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
font-size: .75rem;
|
||||
font-weight: bold;
|
||||
color: var(--white);
|
||||
transition: opacity $transition;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
&:hover .info {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
img {
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.is-load-more-button {
|
||||
margin: 1rem auto 0 !important;
|
||||
display: block;
|
||||
width: 200px;
|
||||
}
|
||||
&:hover .info {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.is-load-more-button {
|
||||
margin: 1rem auto 0 !important;
|
||||
display: block;
|
||||
width: 200px;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,283 +1,236 @@
|
|||
<template>
|
||||
<div class="is-max-width-desktop show-tasks">
|
||||
<fancycheckbox
|
||||
@change="setDate"
|
||||
class="is-pulled-right"
|
||||
v-if="!showAll"
|
||||
v-model="showNulls"
|
||||
>
|
||||
{{ $t('task.show.noDates') }}
|
||||
</fancycheckbox>
|
||||
<h3 v-if="showAll && tasks.length > 0">
|
||||
{{ $t('task.show.current') }}
|
||||
<div class="is-max-width-desktop has-text-left ">
|
||||
<h3 class="mb-2 title">
|
||||
{{ pageTitle }}
|
||||
</h3>
|
||||
<h3 v-else-if="!showAll" class="mb-2">
|
||||
{{ $t('task.show.from') }}
|
||||
<flat-pickr
|
||||
:class="{ 'disabled': loading}"
|
||||
:config="flatPickerConfig"
|
||||
:disabled="loading"
|
||||
@on-close="setDate"
|
||||
class="input"
|
||||
v-model="cStartDate"
|
||||
/>
|
||||
{{ $t('task.show.until') }}
|
||||
<flat-pickr
|
||||
:class="{ 'disabled': loading}"
|
||||
:config="flatPickerConfig"
|
||||
:disabled="loading"
|
||||
@on-close="setDate"
|
||||
class="input"
|
||||
v-model="cEndDate"
|
||||
/>
|
||||
</h3>
|
||||
<div v-if="!showAll" class="mb-4">
|
||||
<x-button variant="secondary" @click="showTodaysTasks()" class="mr-2">{{ $t('task.show.today') }}</x-button>
|
||||
<x-button variant="secondary" @click="setDatesToNextWeek()" class="mr-2">{{ $t('task.show.nextWeek') }}</x-button>
|
||||
<x-button variant="secondary" @click="setDatesToNextMonth()">{{ $t('task.show.nextMonth') }}</x-button>
|
||||
</div>
|
||||
<p v-if="!showAll" class="show-tasks-options">
|
||||
<datepicker-with-range @dateChanged="setDate">
|
||||
<template #trigger="{toggle}">
|
||||
<x-button @click.prevent.stop="toggle()" variant="primary" :shadow="false" class="mb-2">
|
||||
{{ $t('task.show.select') }}
|
||||
</x-button>
|
||||
</template>
|
||||
</datepicker-with-range>
|
||||
<fancycheckbox @change="setShowNulls" class="mr-2">
|
||||
{{ $t('task.show.noDates') }}
|
||||
</fancycheckbox>
|
||||
<fancycheckbox @change="setShowOverdue">
|
||||
{{ $t('task.show.overdue') }}
|
||||
</fancycheckbox>
|
||||
</p>
|
||||
<template v-if="!loading && (!tasks || tasks.length === 0) && showNothingToDo">
|
||||
<h3 class="nothing">{{ $t('task.show.noTasks') }}</h3>
|
||||
<LlamaCool class="llama-cool" />
|
||||
<h3 class="has-text-centered mt-6">{{ $t('task.show.noTasks') }}</h3>
|
||||
<LlamaCool class="llama-cool"/>
|
||||
</template>
|
||||
<div :class="{ 'is-loading': loading}" class="spinner"></div>
|
||||
|
||||
<card :padding="false" class="has-overflow" :has-content="false" v-if="tasks && tasks.length > 0">
|
||||
<div class="tasks">
|
||||
<card
|
||||
v-if="hasTasks"
|
||||
:padding="false"
|
||||
class="has-overflow"
|
||||
:has-content="false"
|
||||
:loading="loading"
|
||||
>
|
||||
<div class="p-2">
|
||||
<single-task-in-list
|
||||
v-for="t in tasksSorted"
|
||||
:key="t.id"
|
||||
class="task"
|
||||
v-for="t in tasks"
|
||||
:show-list="true"
|
||||
:the-task="t"
|
||||
@taskUpdated="updateTasks"/>
|
||||
</div>
|
||||
</card>
|
||||
<div v-else :class="{ 'is-loading': loading}" class="spinner"></div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import SingleTaskInList from '../../components/tasks/partials/singleTaskInList'
|
||||
import {mapState} from 'vuex'
|
||||
|
||||
import flatPickr from 'vue-flatpickr-component'
|
||||
import 'flatpickr/dist/flatpickr.css'
|
||||
import Fancycheckbox from '../../components/input/fancycheckbox'
|
||||
import {LOADING, LOADING_MODULE} from '../../store/mutation-types'
|
||||
<script setup lang="ts">
|
||||
import {computed, ref, watchEffect} from 'vue'
|
||||
import {useStore} from 'vuex'
|
||||
import {useRoute, useRouter} from 'vue-router'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
|
||||
import TaskModel from '@/models/task'
|
||||
import {formatDate} from '@/helpers/time/formatDate'
|
||||
import {setTitle} from '@/helpers/setTitle'
|
||||
|
||||
import Fancycheckbox from '@/components/input/fancycheckbox.vue'
|
||||
import SingleTaskInList from '@/components/tasks/partials/singleTaskInList.vue'
|
||||
import DatepickerWithRange from '@/components/date/datepickerWithRange.vue'
|
||||
import {DATE_RANGES} from '@/components/date/dateRanges'
|
||||
import {LOADING, LOADING_MODULE} from '@/store/mutation-types'
|
||||
import LlamaCool from '@/assets/llama-cool.svg?component'
|
||||
|
||||
export default {
|
||||
name: 'ShowTasks',
|
||||
components: {
|
||||
Fancycheckbox,
|
||||
SingleTaskInList,
|
||||
flatPickr,
|
||||
LlamaCool,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tasks: [],
|
||||
showNulls: true,
|
||||
showOverdue: false,
|
||||
const store = useStore()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const {t} = useI18n()
|
||||
|
||||
cStartDate: null,
|
||||
cEndDate: null,
|
||||
const tasks = ref<TaskModel[]>([])
|
||||
const showNothingToDo = ref<boolean>(false)
|
||||
|
||||
showNothingToDo: false,
|
||||
}
|
||||
},
|
||||
props: {
|
||||
startDate: Date,
|
||||
endDate: Date,
|
||||
showAll: Boolean,
|
||||
},
|
||||
created() {
|
||||
this.cStartDate = this.startDate
|
||||
this.cEndDate = this.endDate
|
||||
this.loadPendingTasks()
|
||||
},
|
||||
mounted() {
|
||||
setTimeout(() => this.showNothingToDo = true, 100)
|
||||
},
|
||||
watch: {
|
||||
'$route': {
|
||||
handler: 'loadPendingTasks',
|
||||
deep: true,
|
||||
},
|
||||
startDate(newVal) {
|
||||
this.cStartDate = newVal
|
||||
},
|
||||
endDate(newVal) {
|
||||
this.cEndDate = newVal
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
flatPickerConfig() {
|
||||
return {
|
||||
altFormat: this.$t('date.altFormatLong'),
|
||||
altInput: true,
|
||||
dateFormat: 'Y-m-d H:i',
|
||||
enableTime: true,
|
||||
time_24hr: true,
|
||||
locale: {
|
||||
firstDayOfWeek: this.$store.state.auth.settings.weekStart,
|
||||
},
|
||||
}
|
||||
},
|
||||
...mapState({
|
||||
userAuthenticated: state => state.auth.authenticated,
|
||||
loading: state => state[LOADING] && state[LOADING_MODULE] === 'tasks',
|
||||
}),
|
||||
},
|
||||
methods: {
|
||||
setDate() {
|
||||
this.$router.push({
|
||||
name: this.$route.name,
|
||||
query: {
|
||||
from: +new Date(this.cStartDate),
|
||||
to: +new Date(this.cEndDate),
|
||||
showOverdue: this.showOverdue,
|
||||
showNulls: this.showNulls,
|
||||
},
|
||||
})
|
||||
},
|
||||
async loadPendingTasks() {
|
||||
// Since this route is authentication only, users would get an error message if they access the page unauthenticated.
|
||||
// Since this component is mounted as the home page before unauthenticated users get redirected
|
||||
// to the login page, they will almost always see the error message.
|
||||
if (!this.userAuthenticated) {
|
||||
return
|
||||
}
|
||||
setTimeout(() => showNothingToDo.value = true, 100)
|
||||
|
||||
// Make sure all dates are date objects
|
||||
if (typeof this.$route.query.from !== 'undefined' && typeof this.$route.query.to !== 'undefined') {
|
||||
this.cStartDate = new Date(Number(this.$route.query.from))
|
||||
this.cEndDate = new Date(Number(this.$route.query.to))
|
||||
} else {
|
||||
this.cStartDate = new Date(this.cStartDate)
|
||||
this.cEndDate = new Date(this.cEndDate)
|
||||
}
|
||||
this.showOverdue = this.$route.query.showOverdue
|
||||
this.showNulls = this.$route.query.showNulls
|
||||
// Linting disabled because we explicitely enabled destructuring in vite's config, this will work.
|
||||
// eslint-disable-next-line vue/no-setup-props-destructure
|
||||
const {
|
||||
dateFrom,
|
||||
dateTo,
|
||||
showNulls = false,
|
||||
showOverdue = false,
|
||||
} = defineProps<{
|
||||
dateFrom?: Date | string,
|
||||
dateTo?: Date | string,
|
||||
showNulls?: Boolean,
|
||||
showOverdue?: Boolean,
|
||||
}>()
|
||||
|
||||
if (this.showAll) {
|
||||
this.setTitle(this.$t('task.show.titleCurrent'))
|
||||
} else {
|
||||
this.setTitle(this.$t('task.show.titleDates', {
|
||||
from: this.cStartDate.toLocaleDateString(),
|
||||
to: this.cEndDate.toLocaleDateString(),
|
||||
}))
|
||||
}
|
||||
const showAll = computed(() => typeof dateFrom === 'undefined' || typeof dateTo === 'undefined')
|
||||
|
||||
const params = {
|
||||
sort_by: ['due_date', 'id'],
|
||||
order_by: ['desc', 'desc'],
|
||||
filter_by: ['done'],
|
||||
filter_value: [false],
|
||||
filter_comparator: ['equals'],
|
||||
filter_concat: 'and',
|
||||
filter_include_nulls: this.showNulls,
|
||||
}
|
||||
if (!this.showAll) {
|
||||
if (this.showNulls) {
|
||||
params.filter_by.push('start_date')
|
||||
params.filter_value.push(this.cStartDate)
|
||||
params.filter_comparator.push('greater')
|
||||
const pageTitle = computed(() => {
|
||||
// We need to define "key" because it is the first parameter in the array and we need the second
|
||||
const predefinedRange = Object.entries(DATE_RANGES)
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
.find(([key, value]) => dateFrom === value[0] && dateTo === value[1])
|
||||
?.[0]
|
||||
if (typeof predefinedRange !== 'undefined') {
|
||||
return t(`input.datepickerRange.ranges.${predefinedRange}`)
|
||||
}
|
||||
|
||||
params.filter_by.push('end_date')
|
||||
params.filter_value.push(this.cEndDate)
|
||||
params.filter_comparator.push('less')
|
||||
}
|
||||
return showAll.value
|
||||
? t('task.show.titleCurrent')
|
||||
: t('task.show.fromuntil', {
|
||||
from: formatDate(dateFrom, 'PPP'),
|
||||
until: formatDate(dateTo, 'PPP'),
|
||||
})
|
||||
})
|
||||
const tasksSorted = computed(() => {
|
||||
// Sort all tasks to put those with a due date before the ones without a due date, the
|
||||
// soonest before the later ones.
|
||||
// We can't use the api sorting here because that sorts tasks with a due date after
|
||||
// ones without a due date.
|
||||
|
||||
params.filter_by.push('due_date')
|
||||
params.filter_value.push(this.cEndDate)
|
||||
params.filter_comparator.push('less')
|
||||
const tasksWithDueDate = [...tasks.value]
|
||||
.filter(t => t.dueDate !== null)
|
||||
.sort((a, b) => {
|
||||
const sortByDueDate = a.dueDate - b.dueDate
|
||||
return sortByDueDate === 0
|
||||
? b.id - a.id
|
||||
: sortByDueDate
|
||||
})
|
||||
const tasksWithoutDueDate = [...tasks.value]
|
||||
.filter(t => t.dueDate === null)
|
||||
|
||||
if (!this.showOverdue) {
|
||||
params.filter_by.push('due_date')
|
||||
params.filter_value.push(this.cStartDate)
|
||||
params.filter_comparator.push('greater')
|
||||
}
|
||||
}
|
||||
return [
|
||||
...tasksWithDueDate,
|
||||
...tasksWithoutDueDate,
|
||||
]
|
||||
})
|
||||
const hasTasks = computed(() => tasks.value && tasks.value.length > 0)
|
||||
const userAuthenticated = computed(() => store.state.auth.authenticated)
|
||||
const loading = computed(() => store.state[LOADING] && store.state[LOADING_MODULE] === 'tasks')
|
||||
|
||||
const tasks = await this.$store.dispatch('tasks/loadTasks', params)
|
||||
|
||||
// FIXME: sort tasks in computed
|
||||
// Sort all tasks to put those with a due date before the ones without a due date, the
|
||||
// soonest before the later ones.
|
||||
// We can't use the api sorting here because that sorts tasks with a due date after
|
||||
// ones without a due date.
|
||||
this.tasks = tasks.sort((a, b) => {
|
||||
const sortByDueDate = b.dueDate - a.dueDate
|
||||
return sortByDueDate === 0
|
||||
? b.id - a.id
|
||||
: sortByDueDate
|
||||
})
|
||||
},
|
||||
|
||||
// FIXME: this modification should happen in the store
|
||||
updateTasks(updatedTask) {
|
||||
for (const t in this.tasks) {
|
||||
if (this.tasks[t].id === updatedTask.id) {
|
||||
this.tasks[t] = updatedTask
|
||||
// Move the task to the end of the done tasks if it is now done
|
||||
if (updatedTask.done) {
|
||||
this.tasks.splice(t, 1)
|
||||
this.tasks.push(updatedTask)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
setDatesToNextWeek() {
|
||||
const now = new Date()
|
||||
this.cStartDate = now
|
||||
this.cEndDate = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000)
|
||||
this.showOverdue = false
|
||||
this.setDate()
|
||||
},
|
||||
|
||||
setDatesToNextMonth() {
|
||||
const now = new Date()
|
||||
this.cStartDate = now
|
||||
this.cEndDate = new Date((new Date()).setMonth(now.getMonth() + 1))
|
||||
this.showOverdue = false
|
||||
this.setDate()
|
||||
},
|
||||
|
||||
showTodaysTasks() {
|
||||
const now = new Date()
|
||||
this.cStartDate = now
|
||||
this.cEndDate = new Date((new Date()).setDate(now.getDate() + 1))
|
||||
this.showOverdue = true
|
||||
this.setDate()
|
||||
},
|
||||
},
|
||||
interface dateStrings {
|
||||
dateFrom: string,
|
||||
dateTo: string,
|
||||
}
|
||||
|
||||
function setDate(dates: dateStrings) {
|
||||
router.push({
|
||||
name: route.name as string,
|
||||
query: {
|
||||
from: dates.dateFrom ?? dateFrom,
|
||||
to: dates.dateTo ?? dateTo,
|
||||
showOverdue: showOverdue ? 'true' : 'false',
|
||||
showNulls: showNulls ? 'true' : 'false',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
function setShowOverdue(show: boolean) {
|
||||
router.push({
|
||||
name: route.name as string,
|
||||
query: {
|
||||
...route.query,
|
||||
showOverdue: show ? 'true' : 'false',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
function setShowNulls(show: boolean) {
|
||||
router.push({
|
||||
name: route.name as string,
|
||||
query: {
|
||||
...route.query,
|
||||
showNulls: show ? 'true' : 'false',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async function loadPendingTasks(from: string, to: string) {
|
||||
// FIXME: HACK! This should never happen.
|
||||
// Since this route is authentication only, users would get an error message if they access the page unauthenticated.
|
||||
// Since this component is mounted as the home page before unauthenticated users get redirected
|
||||
// to the login page, they will almost always see the error message.
|
||||
if (!userAuthenticated.value) {
|
||||
return
|
||||
}
|
||||
|
||||
const params = {
|
||||
sortBy: ['due_date', 'id'],
|
||||
orderBy: ['desc', 'desc'],
|
||||
filterBy: ['done'],
|
||||
filterValue: ['false'],
|
||||
filterComparator: ['equals'],
|
||||
filterConcat: 'and',
|
||||
filterIncludeNulls: showNulls,
|
||||
}
|
||||
|
||||
if (!showAll.value) {
|
||||
params.filterBy.push('due_date')
|
||||
params.filterValue.push(to)
|
||||
params.filterComparator.push('less')
|
||||
|
||||
// NOTE: Ideally we could also show tasks with a start or end date in the specified range, but the api
|
||||
// is not capable (yet) of combining multiple filters with 'and' and 'or'.
|
||||
|
||||
if (!showOverdue) {
|
||||
params.filterBy.push('due_date')
|
||||
params.filterValue.push(from)
|
||||
params.filterComparator.push('greater')
|
||||
}
|
||||
}
|
||||
|
||||
tasks.value = await store.dispatch('tasks/loadTasks', params)
|
||||
}
|
||||
|
||||
// FIXME: this modification should happen in the store
|
||||
function updateTasks(updatedTask: TaskModel) {
|
||||
for (const t in tasks.value) {
|
||||
if (tasks.value[t].id === updatedTask.id) {
|
||||
tasks.value[t] = updatedTask
|
||||
// Move the task to the end of the done tasks if it is now done
|
||||
if (updatedTask.done) {
|
||||
tasks.value.splice(t, 1)
|
||||
tasks.value.push(updatedTask)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
watchEffect(() => loadPendingTasks(dateFrom as string, dateTo as string))
|
||||
watchEffect(() => setTitle(pageTitle.value))
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
h3 {
|
||||
text-align: left;
|
||||
|
||||
&.nothing {
|
||||
text-align: center;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
:deep(.input) {
|
||||
width: 190px;
|
||||
vertical-align: middle;
|
||||
margin: .5rem 0;
|
||||
}
|
||||
}
|
||||
|
||||
.tasks {
|
||||
padding: .5rem;
|
||||
.show-tasks-options {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.llama-cool {
|
||||
margin-top: 2rem;
|
||||
margin: 3rem auto 0;
|
||||
display: block;
|
||||
}
|
||||
</style>
|
|
@ -1,20 +0,0 @@
|
|||
<template>
|
||||
<div class="content has-text-centered">
|
||||
<ShowTasks
|
||||
:end-date="endDate"
|
||||
:start-date="startDate"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import ShowTasks from './ShowTasks.vue'
|
||||
|
||||
function getNextWeekDate() {
|
||||
return new Date((new Date()).getTime() + 7 * 24 * 60 * 60 * 1000)
|
||||
}
|
||||
|
||||
const startDate = ref(new Date())
|
||||
const endDate = ref(getNextWeekDate())
|
||||
</script>
|
|
@ -72,7 +72,7 @@
|
|||
</transition>
|
||||
<transition name="flash-background" appear>
|
||||
<div class="column" v-if="activeFields.percentDone">
|
||||
<!-- Percent Done -->
|
||||
<!-- Progress -->
|
||||
<div class="detail-title">
|
||||
<icon icon="percent"/>
|
||||
{{ $t('task.attributes.percentDone') }}
|
||||
|
@ -259,6 +259,7 @@
|
|||
class="is-outlined has-no-border"
|
||||
icon="check-double"
|
||||
variant="secondary"
|
||||
v-shortcut="'t'"
|
||||
>
|
||||
{{ task.done ? $t('task.detail.undone') : $t('task.detail.done') }}
|
||||
</x-button>
|
||||
|
@ -356,6 +357,7 @@
|
|||
@click="setFieldActive('moveList')"
|
||||
variant="secondary"
|
||||
icon="list"
|
||||
v-shortcut="'m'"
|
||||
>
|
||||
{{ $t('task.detail.actions.moveList') }}
|
||||
</x-button>
|
||||
|
@ -363,6 +365,7 @@
|
|||
@click="setFieldActive('color')"
|
||||
variant="secondary"
|
||||
icon="fill-drip"
|
||||
v-shortcut="'c'"
|
||||
>
|
||||
{{ $t('task.detail.actions.color') }}
|
||||
</x-button>
|
||||
|
@ -520,6 +523,13 @@ export default {
|
|||
},
|
||||
immediate: true,
|
||||
},
|
||||
// Using a watcher here because the header component handles saving the task with the api but we want to decouple
|
||||
// it from the page title.
|
||||
'task.title': {
|
||||
handler(title) {
|
||||
this.setTitle(title)
|
||||
},
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
currentList() {
|
||||
|
|
|
@ -181,7 +181,7 @@ export default {
|
|||
validateField() {
|
||||
// using computed so that debounced function definition stays
|
||||
return useDebounceFn((field) => {
|
||||
this[`${field}Valid`] = this.$refs[field].value !== ''
|
||||
this[`${field}Valid`] = this.$refs[field]?.value !== ''
|
||||
}, 100)
|
||||
},
|
||||
},
|
||||
|
|
|
@ -135,7 +135,7 @@
|
|||
import {computed, watch, ref} from 'vue'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
|
||||
import {playSoundWhenDoneKey, playPop} from '@/helpers/playPop'
|
||||
import {playSoundWhenDoneKey, playPopSound} from '@/helpers/playPop'
|
||||
import {availableLanguages} from '@/i18n'
|
||||
import {getQuickAddMagicMode, setQuickAddMagicMode} from '@/helpers/quickAddMagicMode'
|
||||
import {PrefixMode} from '@/modules/parseTaskText'
|
||||
|
@ -230,13 +230,13 @@ export default {
|
|||
watch: {
|
||||
playSoundWhenDone(play) {
|
||||
if (play) {
|
||||
playPop()
|
||||
playPopSound()
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async updateSettings() {
|
||||
localStorage.setItem(playSoundWhenDoneKey, this.playSoundWhenDone)
|
||||
localStorage.setItem(playSoundWhenDoneKey, this.playSoundWhenDone ? 'true' : 'false')
|
||||
setQuickAddMagicMode(this.quickAddMagicMode)
|
||||
|
||||
await this.$store.dispatch('auth/saveUserSettings', {
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
"resolveJsonModule": true,
|
||||
"sourceMap": true,
|
||||
"baseUrl": ".",
|
||||
"strictNullChecks": true,
|
||||
"isolatedModules": true,
|
||||
"types": [
|
||||
"vite/client"
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
/// <reference types="vitest" />
|
||||
import { defineConfig } from 'vite'
|
||||
import {defineConfig} from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import legacyFn from '@vitejs/plugin-legacy'
|
||||
|
||||
const {VitePWA} = require('vite-plugin-pwa')
|
||||
const path = require('path')
|
||||
const {visualizer} = require('rollup-plugin-visualizer')
|
||||
|
@ -49,6 +50,7 @@ export default defineConfig({
|
|||
},
|
||||
},
|
||||
},
|
||||
reactivityTransform: true,
|
||||
}),
|
||||
legacy,
|
||||
svgLoader({
|
||||
|
@ -130,7 +132,7 @@ export default defineConfig({
|
|||
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'],
|
||||
},
|
||||
server: {
|
||||
port: 5000,
|
||||
port: 4173,
|
||||
strictPort: true,
|
||||
},
|
||||
build: {
|
||||
|
|
Reference in New Issue