Compare commits

..
Author SHA1 Message Date
Dominik Pschenitschni b7665b95cd wip: move to bulma 1.0 2024-11-02 19:32:04 +01:00
kolaente de1eac5d36 fix: pin xgo to 1.22.x
Hopefully resolves build issues like https://drone.kolaente.de/vikunja/vikunja/8996/3/10
2024-11-02 19:00:02 +01:00
kolaente 3f62c013ba fix(typesense): fetch task comments without permission check
Fetching the task comments during indexing would always check the permissions - in the specific case of indexing comments into Typesense, this will always return true, because we're checking with the owner of the project. Because this is a rather expensive operation, it is even more unnecessary.
2024-11-02 18:42:12 +01:00
Dominik Pschenitschniandkonrad e8bf5e33f7 feat: use sass-embedded 2024-11-02 17:39:20 +00:00
kolaente 4238a5f6a3 fix: download badge in readme
Resolves https://github.com/go-vikunja/vikunja/issues/345
2024-11-02 18:22:29 +01:00
renovate c909bfcb8e fix(deps): update module github.com/threedotslabs/watermill to v1.4.1 2024-11-02 16:46:48 +00:00
Dominik Pschenitschniandkonrad 49fa32aad6 feat: use position sticky for demo bar 2024-11-02 08:20:57 +00:00
renovateandkonrad 216177d873 chore(deps): update dev-dependencies 2024-11-02 08:19:37 +00:00
Dominik Pschenitschni f3e77eb1f0 fix: remove defineProps 2024-11-01 17:19:28 +01:00
Frederick [Bot]anddrone 78379637d4 chore(i18n): update translations via Crowdin 2024-11-01 00:03:09 +00:00
kolaente e393cfdfd7 fix(views): delete task buckets and task positions as well when deleting a view 2024-10-31 19:22:51 +01:00
kolaente 1fac81c8de feat(cli): add cli command to delete orphan task positions 2024-10-31 19:19:56 +01:00
kolaente d03f9c7b73 fix(positions): directly look in the database to fetch tasks when recalculating their position 2024-10-31 19:02:15 +01:00
kolaente d7eff8e43b feat(positions): add more debug logs 2024-10-31 18:46:09 +01:00
renovateandkonrad f1f1f669b3 fix(deps): update dependency @sentry/vue to v8.36.0 2024-10-31 10:24:16 +00:00
renovate 03cda24bcf chore(deps): update dev-dependencies 2024-10-31 06:16:07 +00:00
Frederick [Bot]anddrone 004d5709d0 chore(i18n): update translations via Crowdin 2024-10-31 00:04:23 +00:00
renovate 6794547942 fix(deps): update vueuse to v11.2.0 2024-10-30 20:18:45 +00:00
kolaente 32bdef841f fix: lint 2024-10-30 15:45:37 +01:00
kolaente e766b63d99 feat(kanban): add debug option to show task position on card
This will hopefully help debugging heisenbugs which only happen in production
2024-10-30 15:44:36 +01:00
kolaente 796199827e feat(filters): show when the current view has a filter as well and both will be used
This should make it clear when a filter does not bring the expected results - maybe because it contradicts with the one set in the view.
Related to https://github.com/go-vikunja/vikunja/issues/296
2024-10-30 15:27:33 +01:00
kolaente 7f5f5a4b8d fix(events): do not crash filter event handler when triggered by a link share user
Resolves https://vikunja.sentry.io/issues/6030883119/events/4013802889b64b6b9d868e7c83ba4eb5/
2024-10-30 14:49:33 +01:00
kolaente 8522eb9b0d docs(filters): fix typos with filter query docs 2024-10-30 14:30:10 +01:00
kolaente 3c22b3cf00 feat(kanban): show project on kanban card if it's not the same as the current one
Resolves https://community.vikunja.io/t/feature-requests-regarding-view-ordering-project-reference-in-kanban-and-a-notin-operator/2728
2024-10-30 13:48:21 +01:00
kolaente d081cf80fc chore(caldav): refactor fetching projects 2024-10-30 08:37:27 +01:00
kolaente 3ce3727a5e chore(ci): sign drone config 2024-10-30 07:34:13 +00:00
renovateandkonrad c36397188c chore(deps): update node.js to v22.11.0 2024-10-30 07:34:13 +00:00
konrad 564852ab12 chore(deps): set workbox version to 7.3.0 2024-10-30 06:26:34 +00:00
renovate 7aed582965 chore(deps): update dev-dependencies 2024-10-30 00:15:46 +00:00
renovate 70bd12badc fix(deps): update dependency pinia to v2.2.5 2024-10-29 20:15:44 +00:00
renovateandkonrad 2c486a30a0 chore(deps): update dependency node to v22 2024-10-29 19:15:07 +00:00
renovateandkonrad d1df1a3174 fix(deps): update dependency workbox-precaching to v7.3.0 2024-10-29 19:10:23 +00:00
renovateandkonrad de222f290a fix(deps): update module github.com/threedotslabs/watermill to v1.4.0 2024-10-29 19:10:03 +00:00
kolaente b9366ca9e3 fix(filter): use correct syntax for not in query in typesense
Related to https://github.com/typesense/typesense/issues/2038
2024-10-29 17:23:16 +01:00
kolaente 57c6f2cd10 feat(filters): add "not in" operator for filters
Resolves https://community.vikunja.io/t/feature-requests-regarding-view-ordering-project-reference-in-kanban-and-a-notin-operator/2728
2024-10-29 12:03:16 +01:00
kolaente aad9d8dffc fix(tests): faker usage 2024-10-29 09:57:53 +00:00
kolaente 2c8e636561 chore(deps): update devenv 2024-10-29 09:57:53 +00:00
kolaente d4a5d1ecdf chore: remove unused rushstack eslint patch 2024-10-29 09:57:53 +00:00
kolaente 703c641aeb fix: lint issues 2024-10-29 09:57:53 +00:00
kolaente b601671395 chore: migrate eslint config 2024-10-29 09:57:53 +00:00
renovateandkonrad e36578bb89 chore(deps): update dev-dependencies 2024-10-29 09:57:53 +00:00
renovate cf77969550 fix(deps): update module github.com/swaggo/swag to v1.16.4 2024-10-29 09:15:35 +00:00
renovateandkonrad e1718ccb35 chore(deps): update goreleaser/nfpm docker tag to v2.41.0 (#2776)
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2024-10-29 08:35:46 +00:00
kolaente 412a8ef654 chore(projects): only pass users to checks 2024-10-29 09:11:06 +01:00
kolaente 816da6bc50 fix(projects): check with the current user if they have access to the project 2024-10-29 08:40:14 +01:00
renovateandkonrad 21356ae08e chore(deps): update node.js to v22 (#2783)
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2024-10-29 07:38:12 +00:00
kolaente dace07efda fix(projects): return 0 if no parent project exists 2024-10-29 08:25:20 +01:00
Kiril Vladimirovandkonrad 540c5f4225 fix(i18n): capitalize Bulgarian label (#2784)
Also, remove 'език' which means 'language' for the sake of consistency.

Reviewed-on: https://kolaente.dev/vikunja/vikunja/pulls/2784
Reviewed-by: konrad <k@knt.li>
Co-authored-by: Kiril Vladimirov <kiril@vld.bg>
Co-committed-by: Kiril Vladimirov <kiril@vld.bg>
2024-10-29 07:17:28 +00:00
renovate 58491ef55e chore(deps): update dev-dependencies 2024-10-29 00:16:41 +00:00
renovate 8b0ab4758e fix(deps): update dependency @kyvg/vue3-notification to v3.4.1 2024-10-28 18:15:36 +00:00
kolaente fa032a889f fix(filters): do not crash when a filter is invalid
Resolves https://vikunja.sentry.io/issues/6025547266/events/a62beaa09caf482ba238b18f363956df
2024-10-28 16:16:48 +01:00
kolaente e30374a28c fix(projects): only add conditions to query when they are non-empty 2024-10-28 16:09:31 +01:00
kolaente b013b294e2 fix(projects): remove unnecessary join
The removal of the unnecessary join condition speeds up the query 10x. Before, it would take ~700ms on Vikunja Cloud. With this removal, the otherwise same query now takes ~70ms (which still leaves plenty of room for improvements, but it's already a great step forwards).
2024-10-28 15:58:07 +01:00
kolaente acdb45a92c fix: lint 2024-10-28 14:04:54 +01:00
kolaente 29107e9865 fix: error message check on mysql 2024-10-28 13:50:12 +01:00
kolaente a70c472aa3 fix: create missing indexes on postgres 2024-10-28 13:25:38 +01:00
kolaente e1893ff573 chore: cleanup 2024-10-28 12:44:54 +01:00
kolaente 106a1c7864 fix(events): report async errors via Sentry 2024-10-28 12:44:26 +01:00
kolaente fcd56cb7cf feat(i18n): add Bulgarian for language selection 2024-10-28 11:27:42 +01:00
kolaente 7055d7341c feat(sharing): add config so that users only find members of their teams
This adds a feature where you can enable users to only find members of teams they're part of. This makes the user search when sharing projects less confusing, because users only see other users they already know.
It is still possible to add users to teams with their email address, if they have that enabled in the user settings.
2024-10-28 11:08:06 +01:00
renovateandkonrad 6a804e8b2d chore(deps): update pnpm to v9.12.3 2024-10-28 06:16:02 +00:00
renovate a917676e50 chore(deps): update dev-dependencies 2024-10-28 00:15:53 +00:00
Frederick [Bot]anddrone 9c0c5fdd6c chore(i18n): update translations via Crowdin 2024-10-28 00:03:02 +00:00
renovate d42af853ec chore(deps): update dependency caniuse-lite to v1.0.30001672 2024-10-27 00:16:16 +00:00
renovateandkonrad ca1384e3c9 chore(deps): update dev-dependencies (#2754)
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2024-10-26 18:06:51 +00:00
John Doeandkonrad 28d5cd7b28 feat: allow setting schema for connection in postgres (#2777)
This PR introduce a new config for database, `database.schema`, allowing user to specify a specific schema to use for their postgres database connection. As the default value is set, it will be backward compatible.

Related discussion: https://community.vikunja.io/t/postgres-database-has-error-pq-relation-tasks-does-not-exist/1333

Reviewed-on: https://kolaente.dev/vikunja/vikunja/pulls/2777
Co-authored-by: John Doe <hugosum.dev@protonmail.com>
Co-committed-by: John Doe <hugosum.dev@protonmail.com>
2024-10-26 16:06:47 +00:00
kolaente b5cbe99b0a fix(cmd): report error when the connection to the mail server failed
When running the testmail command, Vikunja would not stop if it wasn't able to connect to the mail server. This was a regression from 950de7c954.
This change fixes that problem.

Resolves https://kolaente.dev/vikunja/vikunja/issues/2767
2024-10-25 19:23:39 +02:00
d784e98f8c feat(cmd): Allow to specify custom filename for dump command (#2775)
This simplifies automated postprocessing of the backup file.

Co-authored-by: Maarten <mail@mgrothus.de>
Reviewed-on: https://kolaente.dev/vikunja/vikunja/pulls/2775
Co-authored-by: Sterad <hgasuser@gmail.com>
Co-committed-by: Sterad <hgasuser@gmail.com>
2024-10-25 12:37:17 +00:00
Steradandkonrad 1f76a8bb64 feat: preferably award admin access to project users with write access on user deletion (#2772)
Reviewed-on: https://kolaente.dev/vikunja/vikunja/pulls/2772
Co-authored-by: Sterad <hgasuser@gmail.com>
Co-committed-by: Sterad <hgasuser@gmail.com>
2024-10-24 20:42:38 +00:00
renovate 4b464b3216 fix(deps): update tiptap to v2.9.1 2024-10-23 10:16:00 +00:00
renovate b346a96127 fix(deps): update tiptap to v2.9.0 2024-10-22 14:15:14 +00:00
kolaente 5f5d93dfa0 fix(gantt): reload the gantt chart when switching between projects
Resolves https://github.com/go-vikunja/vikunja/issues/340
2024-10-21 18:32:57 +02:00
Sebastien Danthinneandkonrad 75dddc9dbe fix(attachments): Add .jpeg to previewable image (#2770)
This is necessary to be able to set .jpeg images as the background for kanban tiles. This extension is equivalent to .jpg, and is the default extension for uploading from iOS.

Reviewed-on: https://kolaente.dev/vikunja/vikunja/pulls/2770
Co-authored-by: Sebastien Danthinne <sebastien@danthinne.com>
Co-committed-by: Sebastien Danthinne <sebastien@danthinne.com>
2024-10-21 15:04:17 +00:00
renovate 6a671a7093 fix(deps): update dependency @sentry/vue to v8.35.0 2024-10-21 12:15:48 +00:00
131 changed files with 6092 additions and 3079 deletions
+17 -17
View File
@@ -363,7 +363,7 @@ steps:
- api-build
- name: frontend-dependencies
image: node:20.16.0-alpine
image: node:22.11.0-alpine
pull: always
environment:
PNPM_CACHE_FOLDER: .cache/pnpm
@@ -377,7 +377,7 @@ steps:
# - restore-cache
- name: frontend-lint
image: node:20.16.0-alpine
image: node:22.11.0-alpine
pull: always
environment:
PNPM_CACHE_FOLDER: .cache/pnpm
@@ -389,7 +389,7 @@ steps:
- frontend-dependencies
- name: frontend-build-prod
image: node:20.16.0-alpine
image: node:22.11.0-alpine
pull: always
environment:
PNPM_CACHE_FOLDER: .cache/pnpm
@@ -401,7 +401,7 @@ steps:
- frontend-dependencies
- name: frontend-test-unit
image: node:20.16.0-alpine
image: node:22.11.0-alpine
pull: always
commands:
- cd frontend
@@ -412,7 +412,7 @@ steps:
- name: frontend-typecheck
failure: ignore
image: node:20.16.0-alpine
image: node:22.11.0-alpine
pull: always
environment:
PNPM_CACHE_FOLDER: .cache/pnpm
@@ -543,7 +543,7 @@ steps:
- git fetch --tags
- name: frontend-dependencies
image: node:20.16.0-alpine
image: node:22.11.0-alpine
pull: always
environment:
PNPM_CACHE_FOLDER: .cache/pnpm
@@ -555,7 +555,7 @@ steps:
- pnpm install --fetch-timeout 100000
- name: frontend-build
image: node:20.16.0-alpine
image: node:22.11.0-alpine
pull: always
environment:
PNPM_CACHE_FOLDER: .cache/pnpm
@@ -626,7 +626,7 @@ steps:
depends_on: [ generate-config-yaml ]
- name: before-static-build
image: techknowlogick/xgo:latest
image: techknowlogick/xgo:go-1.22.x
pull: always
commands:
- export PATH=$PATH:$GOPATH/bin
@@ -635,7 +635,7 @@ steps:
depends_on: [ fetch-tags, mage ]
- name: static-build-windows
image: techknowlogick/xgo:latest
image: techknowlogick/xgo:go-1.22.x
pull: always
environment:
# This path does not exist. However, when we set the gopath to /go, the build fails. Not sure why.
@@ -651,7 +651,7 @@ steps:
- frontend-build
- name: static-build-linux
image: techknowlogick/xgo:latest
image: techknowlogick/xgo:go-1.22.x
pull: always
environment:
# This path does not exist. However, when we set the gopath to /go, the build fails. Not sure why.
@@ -667,7 +667,7 @@ steps:
- frontend-build
- name: static-build-darwin
image: techknowlogick/xgo:latest
image: techknowlogick/xgo:go-1.22.x
pull: always
environment:
# This path does not exist. However, when we set the gopath to /go, the build fails. Not sure why.
@@ -693,7 +693,7 @@ steps:
- ./mage-static release:compress
- name: after-build-static
image: techknowlogick/xgo:latest
image: techknowlogick/xgo:go-1.22.x
pull: always
depends_on:
- after-build-compress
@@ -763,7 +763,7 @@ steps:
# Build os packages and push it to our bucket
- name: build-os-packages-unstable
image: goreleaser/nfpm:v2.40.0
image: goreleaser/nfpm:v2.41.0
pull: always
commands:
- apk add git go
@@ -779,7 +779,7 @@ steps:
depends_on: [ after-build-compress ]
- name: build-os-packages-version
image: goreleaser/nfpm:v2.40.0
image: goreleaser/nfpm:v2.41.0
pull: always
commands:
- apk add git go
@@ -948,7 +948,7 @@ steps:
- git fetch --tags
- name: build
image: node:20.16.0-alpine
image: node:22.11.0-alpine
pull: always
environment:
PNPM_CACHE_FOLDER: .cache/pnpm
@@ -1009,7 +1009,7 @@ steps:
- git fetch --tags
- name: build
image: node:20.16.0-alpine
image: node:22.11.0-alpine
pull: always
environment:
PNPM_CACHE_FOLDER: .cache/pnpm
@@ -1400,6 +1400,6 @@ steps:
- failure
---
kind: signature
hmac: ba0652aeee2f07af2f861ea281d156db7ddd4803672dae0101ebb1f8111ec580
hmac: 75759b487ff5f815f3ca3d77e824f7d1d70a9c07d03967f62a00217f421a81d0
...
+2 -2
View File
@@ -1,5 +1,5 @@
# syntax=docker/dockerfile:1
FROM --platform=$BUILDPLATFORM node:20.16.0-alpine AS frontendbuilder
FROM --platform=$BUILDPLATFORM node:22.11.0-alpine AS frontendbuilder
WORKDIR /build
@@ -13,7 +13,7 @@ RUN corepack enable && \
pnpm install && \
pnpm run build
FROM --platform=$BUILDPLATFORM techknowlogick/xgo:go-1.21.x AS apibuilder
FROM --platform=$BUILDPLATFORM techknowlogick/xgo:go-1.22.x AS apibuilder
RUN go install github.com/magefile/mage@latest && \
mv /go/bin/mage /usr/local/go/bin
+1 -1
View File
@@ -2,7 +2,7 @@
[![Build Status](https://drone.kolaente.de/api/badges/vikunja/vikunjaa/status.svg)](https://drone.kolaente.de/vikunja/vikunja)
[![License: AGPL v3](https://img.shields.io/badge/License-AGPL%20v3-blue.svg)](LICENSE)
[![Install](https://img.shields.io/badge/download-v0.24.4,brightgreen.svg)](https://vikunja.io/docs/installing)
[![Install](https://img.shields.io/badge/download-v0.24.4-brightgreen.svg)](https://vikunja.io/docs/installing)
[![Docker Pulls](https://img.shields.io/docker/pulls/vikunja/vikunja.svg)](https://hub.docker.com/r/vikunja/vikunja/)
[![Swagger Docs](https://img.shields.io/badge/swagger-docs-brightgreen.svg)](https://try.vikunja.io/api/v1/docs)
[![Go Report Card](https://goreportcard.com/badge/kolaente.dev/vikunja/vikunja)](https://goreportcard.com/report/kolaente.dev/vikunja/vikunja)
+5
View File
@@ -132,6 +132,11 @@
"key": "bcryptrounds",
"default_value": "11",
"comment": "The number of bcrypt rounds to use during registration. Each increment of this number doubles the computational cost. You probably don't need to change this value."
},
{
"key": "enableopenidteamusersearch",
"default_value": "false",
"comment": "If enabled, users will only find other users who are part of an existing team when they are searching for a user by their partial name. The other existing team may be created from openid. It is still possible to add users to teams with their exact email address even when this is enabled."
}
]
},
+2 -2
View File
@@ -51,8 +51,8 @@
}
},
"devDependencies": {
"electron": "29.4.6",
"electron-builder": "24.13.3"
"electron": "33.0.2",
"electron-builder": "25.1.8"
},
"dependencies": {
"connect-history-api-fallback": "2.0.0",
+855 -28
View File
File diff suppressed because it is too large Load Diff
+8 -15
View File
@@ -3,11 +3,10 @@
"devenv": {
"locked": {
"dir": "src/modules",
"lastModified": 1726063457,
"lastModified": 1730028891,
"owner": "cachix",
"repo": "devenv",
"rev": "39bf6ce569103c9390d37322daa59468c31b3ce7",
"treeHash": "839747a1cb35ba6d5b36cce9a739ab2ba5e4a5d4",
"rev": "a337f8862efa9bde348c48a48b5b943f710604dd",
"type": "github"
},
"original": {
@@ -24,7 +23,6 @@
"owner": "edolstra",
"repo": "flake-compat",
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
"treeHash": "2addb7b71a20a25ea74feeaf5c2f6a6b30898ecb",
"type": "github"
},
"original": {
@@ -45,7 +43,6 @@
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
"treeHash": "ca14199cabdfe1a06a7b1654c76ed49100a689f9",
"type": "github"
},
"original": {
@@ -60,7 +57,6 @@
"owner": "cachix",
"repo": "devenv-nixpkgs",
"rev": "4267e705586473d3e5c8d50299e71503f16a6fb6",
"treeHash": "6d9f1f7ca0faf1bc2eeb397c78a49623260d3412",
"type": "github"
},
"original": {
@@ -72,11 +68,10 @@
},
"nixpkgs-stable": {
"locked": {
"lastModified": 1725930920,
"lastModified": 1729973466,
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "44a71ff39c182edaf25a7ace5c9454e7cba2c658",
"treeHash": "56e93544112b7bb7aa0c3093d537295683ef9148",
"rev": "cd3e8833d70618c4eea8df06f95b364b016d4950",
"type": "github"
},
"original": {
@@ -88,11 +83,10 @@
},
"nixpkgs-unstable": {
"locked": {
"lastModified": 1725983898,
"lastModified": 1729880355,
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "1355a0cbfeac61d785b7183c0caaec1f97361b43",
"treeHash": "d8ce0c1af7690da6161f0991484661331f5bc999",
"rev": "18536bf04cd71abd345f9579158841376fdd0c5a",
"type": "github"
},
"original": {
@@ -112,11 +106,10 @@
"nixpkgs-stable": "nixpkgs-stable"
},
"locked": {
"lastModified": 1725513492,
"lastModified": 1729104314,
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"rev": "7570de7b9b504cfe92025dd1be797bf546f66528",
"treeHash": "4b46d77870afecd8f642541cb4f4927326343b59",
"rev": "3c3e88f0f544d6bb54329832616af7eb971b6be6",
"type": "github"
},
"original": {
-63
View File
@@ -1,63 +0,0 @@
/* eslint-env node */
require('@rushstack/eslint-patch/modern-module-resolution')
module.exports = {
'root': true,
'env': {
'browser': true,
'es2022': true,
'node': true,
},
'extends': [
'eslint:recommended',
'plugin:vue/vue3-recommended',
'@vue/eslint-config-typescript/recommended',
],
'rules': {
'quotes': ['error', 'single'],
'comma-dangle': ['error', 'always-multiline'],
'semi': ['error', 'never'],
'vue/v-on-event-hyphenation': ['warn', 'never', { 'autofix': true }],
'vue/multi-word-component-names': 'off',
// uncategorized rules:
'vue/component-api-style': ['error', ['script-setup']],
'vue/component-name-in-template-casing': ['error', 'PascalCase', {
'globals': ['RouterView', 'RouterLink', 'Icon', 'Notifications', 'Modal', 'Card'],
}],
'vue/custom-event-name-casing': ['error', 'camelCase'],
'vue/define-macros-order': 'error',
'vue/match-component-file-name': ['error', {
'extensions': ['.js', '.jsx', '.ts', '.tsx', '.vue'],
'shouldMatchCase': true,
}],
'vue/no-boolean-default': ['warn', 'default-false'],
'vue/match-component-import-name': 'error',
'vue/prefer-separate-static-class': 'warn',
'vue/padding-line-between-blocks': 'error',
'vue/next-tick-style': ['error', 'promise'],
'vue/block-lang': [
'error',
{ 'script': { 'lang': 'ts' } },
],
'vue/no-duplicate-attr-inheritance': 'error',
'vue/no-empty-component-block': 'error',
'vue/html-indent': ['error', 'tab'],
// vue3
'vue/no-ref-object-reactivity-loss': 'error',
'vue/no-setup-props-reactivity-loss': 'warn', // TODO: switch to error after vite `propsDestructure` is removed
},
'parser': 'vue-eslint-parser',
'parserOptions': {
'parser': '@typescript-eslint/parser',
'ecmaVersion': 'latest',
'tsconfigRootDir': __dirname,
},
'ignorePatterns': [
'*.test.*',
'cypress/*',
],
}
+1 -1
View File
@@ -1 +1 @@
20.16.0
22.11.0
+1 -1
View File
@@ -9,7 +9,7 @@ export class LinkShareFactory extends Factory {
return {
id: '{increment}',
hash: faker.random.word(32),
hash: faker.lorem.word(32),
project_id: 1,
right: 0,
sharing_type: 0,
+1 -1
View File
@@ -10,7 +10,7 @@ export class UserFactory extends Factory {
return {
id: '{increment}',
username: faker.lorem.word(10) + faker.datatype.uuid(),
username: faker.lorem.word(10) + faker.string.uuid(),
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.', // 1234
status: 0,
issuer: 'local',
+88
View File
@@ -0,0 +1,88 @@
import pluginVue from 'eslint-plugin-vue'
import js from '@eslint/js'
import vueTsEslintConfig from '@vue/eslint-config-typescript'
export default [
js.configs.recommended,
...pluginVue.configs['flat/recommended'],
...vueTsEslintConfig(),
{
ignores: [
'**/*.test.ts',
'./cypress',
],
},
{
rules: {
'quotes': ['error', 'single'],
'comma-dangle': ['error', 'always-multiline'],
'semi': ['error', 'never'],
'vue/v-on-event-hyphenation': ['warn', 'never', {'autofix': true}],
'vue/multi-word-component-names': 'off',
// uncategorized rules:
'vue/component-api-style': ['error', ['script-setup']],
'vue/component-name-in-template-casing': ['error', 'PascalCase', {
'globals': ['RouterView', 'RouterLink', 'Icon', 'Notifications', 'Modal', 'Card'],
}],
'vue/custom-event-name-casing': ['error', 'camelCase'],
'vue/define-macros-order': 'error',
'vue/match-component-file-name': ['error', {
'extensions': ['.js', '.jsx', '.ts', '.tsx', '.vue'],
'shouldMatchCase': true,
}],
'vue/no-boolean-default': ['warn', 'default-false'],
'vue/match-component-import-name': 'error',
'vue/prefer-separate-static-class': 'warn',
'vue/padding-line-between-blocks': 'error',
'vue/next-tick-style': ['error', 'promise'],
'vue/block-lang': [
'error',
{'script': {'lang': 'ts'}},
],
'vue/no-duplicate-attr-inheritance': 'error',
'vue/no-empty-component-block': 'error',
'vue/html-indent': ['error', 'tab'],
// vue3
'vue/no-ref-object-reactivity-loss': 'error',
'vue/no-setup-props-reactivity-loss': 'warn', // TODO: switch to error after vite `propsDestructure` is removed
'@typescript-eslint/no-unused-vars': [
'error',
{
// 'args': 'all',
// 'argsIgnorePattern': '^_',
'caughtErrors': 'all',
'caughtErrorsIgnorePattern': '^_',
// 'destructuredArrayIgnorePattern': '^_',
'varsIgnorePattern': '^_',
'ignoreRestSiblings': true,
},
],
},
// files: ['*.vue', '**/*.vue'],
languageOptions: {
parserOptions: {
parser: '@typescript-eslint/parser',
ecmaVersion: 'latest',
tsconfigRootDir: '.',
},
},
// 'parser': 'vue-eslint-parser',
// 'parserOptions': {
// 'parser': '@typescript-eslint/parser',
// 'ecmaVersion': 'latest',
// 'tsconfigRootDir': __dirname,
// },
// 'ignorePatterns': [
// 'cypress/*',
// ],
},
]
+63 -64
View File
@@ -13,7 +13,7 @@
},
"homepage": "https://vikunja.io/",
"funding": "https://opencollective.com/vikunja",
"packageManager": "pnpm@9.12.2",
"packageManager": "pnpm@9.12.3",
"keywords": [
"todo",
"productivity",
@@ -57,48 +57,48 @@
"@github/hotkey": "3.1.1",
"@infectoone/vue-ganttastic": "2.3.2",
"@intlify/unplugin-vue-i18n": "5.2.0",
"@kyvg/vue3-notification": "3.4.0",
"@kyvg/vue3-notification": "3.4.1",
"@sentry/tracing": "7.114.0",
"@sentry/vue": "8.34.0",
"@tiptap/core": "2.8.0",
"@tiptap/extension-blockquote": "2.8.0",
"@tiptap/extension-bold": "2.8.0",
"@tiptap/extension-bullet-list": "2.8.0",
"@tiptap/extension-code": "2.8.0",
"@tiptap/extension-code-block": "2.8.0",
"@tiptap/extension-code-block-lowlight": "2.8.0",
"@tiptap/extension-document": "2.8.0",
"@tiptap/extension-dropcursor": "2.8.0",
"@tiptap/extension-gapcursor": "2.8.0",
"@tiptap/extension-hard-break": "2.8.0",
"@tiptap/extension-heading": "2.8.0",
"@tiptap/extension-history": "2.8.0",
"@tiptap/extension-horizontal-rule": "2.8.0",
"@tiptap/extension-image": "2.8.0",
"@tiptap/extension-italic": "2.8.0",
"@tiptap/extension-link": "2.8.0",
"@tiptap/extension-list-item": "2.8.0",
"@tiptap/extension-ordered-list": "2.8.0",
"@tiptap/extension-paragraph": "2.8.0",
"@tiptap/extension-placeholder": "2.8.0",
"@tiptap/extension-strike": "2.8.0",
"@tiptap/extension-table": "2.8.0",
"@tiptap/extension-table-cell": "2.8.0",
"@tiptap/extension-table-header": "2.8.0",
"@tiptap/extension-table-row": "2.8.0",
"@tiptap/extension-task-item": "2.8.0",
"@tiptap/extension-task-list": "2.8.0",
"@tiptap/extension-text": "2.8.0",
"@tiptap/extension-typography": "2.8.0",
"@tiptap/extension-underline": "2.8.0",
"@tiptap/pm": "2.8.0",
"@tiptap/suggestion": "2.8.0",
"@tiptap/vue-3": "2.8.0",
"@vueuse/core": "11.1.0",
"@vueuse/router": "11.1.0",
"@sentry/vue": "8.36.0",
"@tiptap/core": "2.9.1",
"@tiptap/extension-blockquote": "2.9.1",
"@tiptap/extension-bold": "2.9.1",
"@tiptap/extension-bullet-list": "2.9.1",
"@tiptap/extension-code": "2.9.1",
"@tiptap/extension-code-block": "2.9.1",
"@tiptap/extension-code-block-lowlight": "2.9.1",
"@tiptap/extension-document": "2.9.1",
"@tiptap/extension-dropcursor": "2.9.1",
"@tiptap/extension-gapcursor": "2.9.1",
"@tiptap/extension-hard-break": "2.9.1",
"@tiptap/extension-heading": "2.9.1",
"@tiptap/extension-history": "2.9.1",
"@tiptap/extension-horizontal-rule": "2.9.1",
"@tiptap/extension-image": "2.9.1",
"@tiptap/extension-italic": "2.9.1",
"@tiptap/extension-link": "2.9.1",
"@tiptap/extension-list-item": "2.9.1",
"@tiptap/extension-ordered-list": "2.9.1",
"@tiptap/extension-paragraph": "2.9.1",
"@tiptap/extension-placeholder": "2.9.1",
"@tiptap/extension-strike": "2.9.1",
"@tiptap/extension-table": "2.9.1",
"@tiptap/extension-table-cell": "2.9.1",
"@tiptap/extension-table-header": "2.9.1",
"@tiptap/extension-table-row": "2.9.1",
"@tiptap/extension-task-item": "2.9.1",
"@tiptap/extension-task-list": "2.9.1",
"@tiptap/extension-text": "2.9.1",
"@tiptap/extension-typography": "2.9.1",
"@tiptap/extension-underline": "2.9.1",
"@tiptap/pm": "2.9.1",
"@tiptap/suggestion": "2.9.1",
"@tiptap/vue-3": "2.9.1",
"@vueuse/core": "11.2.0",
"@vueuse/router": "11.2.0",
"axios": "1.7.7",
"blurhash": "2.0.5",
"bulma-css-variables": "0.9.33",
"bulma": "^1.0.2",
"change-case": "5.4.4",
"date-fns": "4.1.0",
"dayjs": "1.11.13",
@@ -110,7 +110,7 @@
"is-touch-device": "1.0.1",
"klona": "2.0.6",
"lowlight": "2.9.0",
"pinia": "2.2.4",
"pinia": "2.2.5",
"register-service-worker": "1.7.2",
"sortablejs": "1.15.3",
"tailwindcss": "3.4.14",
@@ -122,60 +122,59 @@
"vue-i18n": "10.0.4",
"vue-router": "4.4.5",
"vuemoji-picker": "0.3.1",
"workbox-precaching": "7.1.0",
"workbox-precaching": "7.3.0",
"zhyswan-vuedraggable": "4.1.3"
},
"devDependencies": {
"@4tw/cypress-drag-drop": "2.2.5",
"@cypress/vite-dev-server": "5.2.0",
"@cypress/vue": "6.0.1",
"@faker-js/faker": "8.4.1",
"@faker-js/faker": "9.1.0",
"@histoire/plugin-screenshot": "0.17.17",
"@histoire/plugin-vue": "0.17.17",
"@rushstack/eslint-patch": "1.10.4",
"@tsconfig/node20": "20.1.4",
"@types/codemirror": "5.60.15",
"@types/dompurify": "3.0.5",
"@types/flexsearch": "0.7.6",
"@types/is-touch-device": "1.0.3",
"@types/lodash.clonedeep": "4.5.9",
"@types/node": "20.16.11",
"@types/node": "22.8.6",
"@types/sortablejs": "1.15.8",
"@typescript-eslint/eslint-plugin": "7.18.0",
"@typescript-eslint/parser": "7.18.0",
"@vitejs/plugin-legacy": "5.4.2",
"@typescript-eslint/eslint-plugin": "8.12.2",
"@typescript-eslint/parser": "8.12.2",
"@vitejs/plugin-legacy": "5.4.3",
"@vitejs/plugin-vue": "5.1.4",
"@vue/eslint-config-typescript": "13.0.0",
"@vue/eslint-config-typescript": "14.1.3",
"@vue/test-utils": "2.4.6",
"@vue/tsconfig": "0.5.1",
"autoprefixer": "10.4.20",
"browserslist": "4.24.0",
"caniuse-lite": "1.0.30001667",
"browserslist": "4.24.2",
"caniuse-lite": "1.0.30001676",
"csstype": "3.1.3",
"cypress": "13.15.0",
"cypress": "13.15.1",
"esbuild": "0.24.0",
"eslint": "8.57.1",
"eslint-plugin-vue": "9.28.0",
"happy-dom": "14.12.3",
"eslint": "9.14.0",
"eslint-plugin-vue": "9.30.0",
"happy-dom": "15.8.0",
"histoire": "0.17.17",
"postcss": "8.4.47",
"postcss-easing-gradients": "3.0.1",
"postcss-easings": "4.0.0",
"postcss-preset-env": "9.6.0",
"rollup": "4.24.0",
"postcss-preset-env": "10.0.9",
"rollup": "4.24.3",
"rollup-plugin-visualizer": "5.12.0",
"sass": "1.79.4",
"sass-embedded": "^1.80.6",
"start-server-and-test": "2.0.8",
"typescript": "5.6.3",
"unplugin-inject-preload": "2.0.5",
"vite": "5.4.8",
"unplugin-inject-preload": "3.0.0",
"vite": "5.4.10",
"vite-plugin-pwa": "0.20.5",
"vite-plugin-sentry": "1.4.0",
"vite-svg-loader": "5.1.0",
"vitest": "1.6.0",
"vue-tsc": "2.1.6",
"wait-on": "7.2.0",
"workbox-cli": "7.1.0"
"vitest": "2.1.4",
"vue-tsc": "2.1.10",
"wait-on": "8.0.1",
"workbox-cli": "7.3.0"
},
"pnpm": {
"patchedDependencies": {
+2816 -2453
View File
File diff suppressed because it is too large Load Diff
+1 -3
View File
@@ -102,6 +102,4 @@ setLanguage(authStore.settings.language)
useColorScheme()
</script>
<style lang="scss">
@import '@/styles/global.scss';
</style>
<style lang="scss" src="@/styles/global.scss" />
@@ -8,7 +8,7 @@
>
<input
type="checkbox"
class="is-sr-only"
class="tw-sr-only"
:checked="modelValue"
:disabled="disabled || undefined"
@change="(event) => emit('update:modelValue', (event.target as HTMLInputElement).checked)"
+2 -4
View File
@@ -51,10 +51,7 @@ const props = withDefaults(defineProps<{
const wrapper = ref<HTMLElement | null>(null)
const computedBackgroundColor = computed(() => {
if (wrapper.value === null) {
return props.backgroundColor || '#fff'
}
return props.backgroundColor || getInheritedBackgroundColor(wrapper.value)
return props.backgroundColor || wrapper.value === null ? '#fff' : getInheritedBackgroundColor(wrapper.value)
})
/**
@@ -73,6 +70,7 @@ function getHeight(el: HTMLElement) {
* https://gist.github.com/paulirish/5d52fb081b3570c81e3a
*/
function forceLayout(el: HTMLElement) {
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
el.offsetTop
}
@@ -36,9 +36,9 @@
</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>
<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>
<h3>{{ $t('input.datemathHelp.supportedUnits') }}</h3>
<table class="table">
@@ -221,6 +221,8 @@ const buttonText = computed<string>(() => {
</script>
<style lang="scss" scoped>
@use "bulma/sass/utilities/css-variables.scss" as cv;
.datepicker-with-range-container {
position: relative;
}
@@ -231,7 +233,7 @@ const buttonText = computed<string>(() => {
border-radius: $radius;
border: 1px solid var(--grey-200);
background-color: var(--white);
box-shadow: $shadow;
box-shadow: cv.getVar("shadow");
&.is-open {
width: 500px;
@@ -168,6 +168,8 @@ const customRangeActive = computed<boolean>(() => {
</script>
<style lang="scss" scoped>
@use "bulma/sass/utilities/css-variables.scss" as cv;
.datepicker-with-range-container {
position: relative;
}
@@ -178,7 +180,7 @@ const customRangeActive = computed<boolean>(() => {
border-radius: $radius;
border: 1px solid var(--grey-200);
background-color: var(--white);
box-shadow: $shadow;
box-shadow: cv.getVar("shadow");
&.is-open {
width: 500px;
+2 -3
View File
@@ -28,10 +28,9 @@ const enabled = computed(() => configStore.demoModeEnabled && !hide.value)
<style scoped lang="scss">
.demo-mode-banner {
position: fixed;
position: sticky;
bottom: 0;
left: 0;
right: 0;
inset-inline: 0;
background: var(--danger);
z-index: 100;
padding: .5rem;
@@ -9,7 +9,7 @@
</template>
<script setup lang="ts">
import {computed, defineProps} from 'vue'
import {computed} from 'vue'
import BaseButton from '@/components/base/BaseButton.vue'
import {POWERED_BY as poweredByUrl} from '@/urls'
@@ -130,6 +130,8 @@ function reset() {
</script>
<style lang="scss" scoped>
@use "bulma/sass/utilities/css-variables.scss" as cv;
.color-picker-container {
display: flex;
justify-content: center;
@@ -156,7 +158,7 @@ function reset() {
overflow: hidden;
border-radius: 100%;
border: $BORDER_WIDTH solid var(--grey-300);
box-shadow: $shadow;
box-shadow: cv.getVar("shadow");
& > * {
grid-row: 1;
+3 -1
View File
@@ -120,6 +120,8 @@ function close() {
</script>
<style lang="scss" scoped>
@use "bulma/sass/utilities/css-variables.scss" as cv;
.datepicker {
input.input {
display: none;
@@ -132,7 +134,7 @@ function close() {
width: 320px;
background: var(--white);
border-radius: $radius;
box-shadow: $shadow;
box-shadow: cv.getVar("shadow");
@media screen and (max-width: ($tablet)) {
width: calc(100vw - 5rem);
@@ -198,13 +198,13 @@ import {setLinkInEditor} from '@/components/input/editor/setLinkInEditor'
const {
modelValue,
uploadCallback,
isEditEnabled = true,
bottomActions = [],
showSave = false,
placeholder = '',
editShortcut = '',
enableDiscardShortcut = false,
} = defineProps<{
isEditEnabled,
bottomActions,
showSave,
placeholder,
editShortcut,
enableDiscardShortcut,
} = withDefaults(defineProps<{
modelValue: string,
uploadCallback?: UploadCallback,
isEditEnabled?: boolean,
@@ -213,7 +213,14 @@ const {
placeholder?: string,
editShortcut?: string,
enableDiscardShortcut?: boolean,
}>()
}>(), {
isEditEnabled: true,
bottomActions: () => [],
showSave: false,
placeholder: '',
editShortcut: '',
enableDiscardShortcut: false,
})
const emit = defineEmits(['update:modelValue', 'save'])
@@ -117,6 +117,7 @@ async function setApiUrl() {
success({message: t('apiConfig.success', {domain: apiDomain.value})})
configureApi.value = false
emit('foundApi', apiUrl.value)
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (e) {
// Still not found, url is still invalid
errorMsg.value = t('apiConfig.error', {domain: apiDomain.value})
+3 -1
View File
@@ -24,6 +24,8 @@ withDefaults(defineProps<{
</script>
<style lang="scss" scoped>
@use 'sass:list';
.progress-bar {
--progress-height: #{$size-normal};
--progress-bar-background-color: var(--border-light, #{$border-light});
@@ -73,7 +75,7 @@ withDefaults(defineProps<{
// Colors
@each $name, $pair in $colors {
$color: nth($pair, 1);
$color: list.nth($pair, 1);
&.is-#{$name} {
--progress-value-background-color: var(--#{$name}, #{$color});
@@ -133,7 +133,7 @@ const highlightedFilterQuery = computed(() => {
}
let labelTitles = [value.trim()]
if (operator === 'in' || operator === '?=') {
if (operator === 'in' || operator === '?=' || operator === 'not in' || operator === '?!=') {
labelTitles = value.split(',').map(v => v.trim())
}
@@ -45,6 +45,7 @@ const showDocs = ref(false)
<li><code>&lt;=</code>: {{ $t('filters.query.help.operators.lessThanOrEqual') }}</li>
<li><code>like</code>: {{ $t('filters.query.help.operators.like') }}</li>
<li><code>in</code>: {{ $t('filters.query.help.operators.in') }}</li>
<li><code>not in</code>: {{ $t('filters.query.help.operators.notIn') }}</li>
</ul>
<p>{{ $t('filters.query.help.logicalOperators.intro') }}</p>
<ul>
@@ -20,6 +20,7 @@
:has-title="true"
class="filter-popup"
:change-immediately="false"
:filter-from-view="filterFromView"
@showResults="showResults"
/>
</Modal>
@@ -31,14 +32,22 @@ import {computed, ref, watch} from 'vue'
import Filters from '@/components/project/partials/Filters.vue'
import {type TaskFilterParams} from '@/services/taskCollection'
import {type IProjectView} from '@/modelTypes/IProjectView'
import {type IProject} from '@/modelTypes/IProject'
import {useProjectStore} from '@/stores/projects'
const props = defineProps<{
modelValue: TaskFilterParams,
projectId?: IProject['id'],
viewId?: IProjectView['id'],
}>()
const emit = defineEmits<{
'update:modelValue': [value: TaskFilterParams]
}>()
const projectStore = useProjectStore()
const value = ref<TaskFilterParams>({})
watch(
@@ -67,6 +76,19 @@ function showResults() {
})
modalOpen.value = false
}
const filterFromView = computed(() => {
if (!props.projectId || !props.viewId) {
return
}
const project = projectStore.projects[props.projectId]
if (!project) {
return
}
const view = project.views.find(v => v.id === props.viewId)
return view?.filter
})
</script>
<style scoped lang="scss">
@@ -10,6 +10,15 @@
@update:modelValue="() => change('modelValue')"
@blur="() => change('blur')"
/>
<div
v-if="filterFromView"
class="tw-text-sm tw-mb-2"
>
{{ $t('filters.fromView') }}
<code>{{ filterFromView }}</code><br>
{{ $t('filters.fromViewBoth') }}
</div>
<div class="field is-flex is-flex-direction-column">
<FancyCheckbox
@@ -64,6 +73,7 @@ const props = withDefaults(defineProps<{
hasTitle?: boolean,
hasFooter?: boolean,
changeImmediately?: boolean,
filterFromView?: string,
}>(), {
hasTitle: false,
hasFooter: true,
@@ -9,6 +9,8 @@
<FilterPopup
v-if="!isSavedFilter(project)"
v-model="params"
:view-id="viewId"
:project-id="projectId"
/>
</div>
</template>
@@ -208,6 +210,7 @@
class="kanban-card"
:task="task"
:loading="taskUpdating[task.id] ?? false"
:project-id="projectId"
/>
</div>
</template>
@@ -9,6 +9,8 @@
<FilterPopup
v-if="!isSavedFilter(project)"
v-model="params"
:view-id="viewId"
:project-id="projectId"
@update:modelValue="prepareFiltersAndLoadTasks()"
/>
</div>
@@ -67,7 +67,12 @@
</Card>
</template>
</Popup>
<FilterPopup v-model="params" />
<FilterPopup
v-if="!isSavedFilter({id: projectId})"
v-model="params"
:view-id="viewId"
:project-id="projectId"
/>
</div>
</template>
@@ -290,6 +295,7 @@ import type {IProject} from '@/modelTypes/IProject'
import AssigneeList from '@/components/tasks/partials/AssigneeList.vue'
import type {IProjectView} from '@/modelTypes/IProjectView'
import { camelCase } from 'change-case'
import {isSavedFilter} from '@/services/savedFilter'
const props = defineProps<{
projectId: IProject['id'],
@@ -250,7 +250,6 @@ const sharableName = computed(() => {
if (props.shareType === 'user') {
searchService = shallowReactive(new UserService())
// eslint-disable-next-line vue/no-ref-as-operand
sharable = ref(new UserModel())
searchLabel.value = 'username'
@@ -262,7 +261,6 @@ if (props.shareType === 'user') {
}
} else if (props.shareType === 'team') {
searchService = new TeamService()
// eslint-disable-next-line vue/no-ref-as-operand
sharable = ref(new TeamModel())
searchLabel.value = 'name'
@@ -19,19 +19,30 @@
class="tw-w-full"
>
<div class="p-2">
<span class="task-id">
<Done
class="kanban-card__done"
:is-done="task.done"
variant="small"
/>
<template v-if="task.identifier === ''">
#{{ task.index }}
</template>
<template v-else>
{{ task.identifier }}
</template>
</span>
<div class="task-id tw-flex tw-justify-between">
<div>
<Done
class="kanban-card__done"
:is-done="task.done"
variant="small"
/>
<template v-if="task.identifier === ''">
#{{ task.index }}
</template>
<template v-else>
{{ task.identifier }}
</template>
<span
v-if="showTaskPosition"
class="tw-text-red-600 tw-pl-2"
>
{{ task.position }}
</span>
</div>
<div v-if="projectTitle">
{{ projectTitle }}
</div>
</div>
<span
v-if="task.dueDate > 0"
v-tooltip="formatDateLong(task.dueDate)"
@@ -104,6 +115,7 @@ import ChecklistSummary from './ChecklistSummary.vue'
import {getHexColor} from '@/models/task'
import type {ITask} from '@/modelTypes/ITask'
import type {IProject} from '@/modelTypes/IProject'
import {SUPPORTED_IMAGE_SUFFIX} from '@/models/attachment'
import AttachmentService, {PREVIEW_SIZE} from '@/services/attachment'
@@ -113,9 +125,11 @@ import {useTaskStore} from '@/stores/tasks'
import AssigneeList from '@/components/tasks/partials/AssigneeList.vue'
import {playPopSound} from '@/helpers/playPop'
import {isEditorContentEmpty} from '@/helpers/editorContentEmpty'
import {useProjectStore} from '@/stores/projects'
const props = withDefaults(defineProps<{
task: ITask,
projectId: IProject['id'],
loading: boolean,
}>(), {
loading: false,
@@ -127,6 +141,19 @@ const loadingInternal = ref(false)
const color = computed(() => getHexColor(props.task.hexColor))
const projectStore = useProjectStore()
const projectTitle = computed(() => {
if (props.projectId === props.task.projectId) {
return false
}
const project = projectStore.projects[props.task.projectId]
return project?.title
})
const showTaskPosition = computed(() => window.DEBUG_TASK_POSITION)
async function toggleTaskDone(task: ITask) {
loadingInternal.value = true
try {
+6 -3
View File
@@ -5,9 +5,12 @@ export function useBodyClass(className: string, defaultValue = false) {
const isActive = ref(defaultValue)
watchEffect(() => {
isActive.value
? document.body.classList.add(className)
: document.body.classList.remove(className)
if(isActive.value) {
document.body.classList.add(className)
return
}
document.body.classList.remove(className)
})
tryOnBeforeUnmount(() => isActive.value && document.body.classList.remove(className))
@@ -24,7 +24,8 @@ export function useCopyToClipboard() {
if (!successful) {
throw new Error()
}
} catch (err) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (e) {
error(t('misc.copyError'))
}
@@ -38,6 +39,7 @@ export function useCopyToClipboard() {
}
try {
await navigator.clipboard.writeText(text)
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch(e) {
error(t('misc.copyError'))
}
@@ -40,6 +40,7 @@ export const checkAndSetApiUrl = (url: string | undefined | null): Promise<strin
let urlToCheck: URL
try {
urlToCheck = new URL(url)
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (e) {
throw new InvalidApiUrlProvidedError()
}
+20
View File
@@ -67,6 +67,16 @@ describe('Filter Transformation', () => {
expect(transformed).toBe('labels in 1, 2 && due_date = now')
})
it('should correctly resolve multiple labels with a not in clause', () => {
const transformed = transformFilterStringForApi(
'labels not in lorem, ipsum && dueDate = now',
multipleDummyResolver,
nullTitleToIdResolver,
)
expect(transformed).toBe('labels not in 1, 2 && due_date = now')
})
it('should correctly resolve projects', () => {
const transformed = transformFilterStringForApi(
@@ -218,7 +228,17 @@ describe('Filter Transformation', () => {
expect(transformed).toBe('labels in lorem, ipsum')
})
it('should correctly resolve multiple labels not in', () => {
const transformed = transformFilterStringFromApi(
'labels not in 1, 2',
multipleIdToTitleResolver,
nullIdToTitleResolver,
)
expect(transformed).toBe('labels not in lorem, ipsum')
})
it('should not touch the label value when it is undefined', () => {
const transformed = transformFilterStringFromApi(
'labels = one',
+7 -6
View File
@@ -46,6 +46,7 @@ export const FILTER_OPERATORS = [
'<',
'<=',
'like',
'not in',
'in',
'?=',
]
@@ -57,7 +58,7 @@ export const FILTER_JOIN_OPERATOR = [
')',
]
export const FILTER_OPERATORS_REGEX = '(&lt;|&gt;|&lt;=|&gt;=|=|!=|in)'
export const FILTER_OPERATORS_REGEX = '(&lt;|&gt;|&lt;=|&gt;=|=|!=|not in|in)'
export function getFilterFieldRegexPattern(field: string): RegExp {
return new RegExp('(' + field + '\\s*' + FILTER_OPERATORS_REGEX + '\\s*)([\'"]?)([^\'"&|()<]+\\1?)?', 'ig')
@@ -68,13 +69,13 @@ export function transformFilterStringForApi(
labelResolver: (title: string) => number | null,
projectResolver: (title: string) => number | null,
): string {
filter = filter.trim()
if (filter === '') {
return ''
}
AVAILABLE_FILTER_FIELDS.forEach(f => {
filter = filter.replace(new RegExp(f, 'ig'), f)
})
@@ -97,10 +98,10 @@ export function transformFilterStringForApi(
}
let keywords = [keyword.trim()]
if (operator === 'in' || operator === '?=') {
if (operator === 'in' || operator === '?=' || operator === 'not in' || operator === '?!=') {
keywords = keyword.trim().split(',').map(k => k.trim())
}
let replaced = keyword
keywords.forEach(k => {
@@ -162,7 +163,7 @@ export function transformFilterStringFromApi(
const [matched, prefix, operator, space, keyword] = match
if (keyword) {
let keywords = [keyword.trim()]
if (operator === 'in' || operator === '?=') {
if (operator === 'in' || operator === '?=' || operator === 'not in' || operator === '?!=') {
keywords = keyword.trim().split(',').map(k => k.trim())
}
+1 -1
View File
@@ -1,5 +1,5 @@
import {describe, it, expect} from 'vitest'
import {hourToDaytime} from "./hourToDaytime"
import {hourToDaytime} from './hourToDaytime'
function dateWithHour(hours: number): Date {
const newDate = new Date()
+1 -1
View File
@@ -23,7 +23,7 @@ export function parseDateProp(kebabDate: DateKebab | undefined): string | undefi
throw new Error('Invalid date values')
}
return new Date(year, month - 1, date).toISOString() as DateISO
} catch(e) {
} catch(_) {
// ignore nonsense route queries
return
}
+1
View File
@@ -26,6 +26,7 @@ export const SUPPORTED_LOCALES = {
'hr-HR': 'Hrvatski',
'uk-UA': 'Українська',
'lt-LT': 'Lietuvių Kalba',
'bg-BG': 'Български',
// IMPORTANT: Also add new languages to useDayjsLanguageSync
} as const
+5 -2
View File
@@ -410,6 +410,8 @@
"title": "الفلاتر",
"clear": "مسح الفلاتر",
"showResults": "Show results",
"fromView": "The current view has a filter set as well:",
"fromViewBoth": "It will be used in combination with what you enter here.",
"attributes": {
"title": "العنوان",
"titlePlaceholder": "عنوان الفلتر المحفوظ هنا…",
@@ -447,7 +449,7 @@
"help": {
"intro": "To filter tasks, you can use a query syntax similar to SQL. The available fields for filtering include:",
"link": "How does this work?",
"canUseDatemath": "You can date math to set relative dates. Click on the date value in a query to find out more.",
"canUseDatemath": "You can use date math to set relative dates. Click on the date value in a query to find out more.",
"fields": {
"done": "Whether the task is completed or not",
"priority": "The priority level of the task (1-5)",
@@ -472,7 +474,8 @@
"lessThan": "Less than",
"lessThanOrEqual": "Less than or equal to",
"like": "Matches a pattern (using wildcard %)",
"in": "Matches any value in a comma-seperated list of values"
"in": "Matches any value in a comma-seperated list of values",
"notIn": "Matches any value not present in a comma-seperated list of values"
},
"logicalOperators": {
"intro": "To combine multiple conditions, you can use the following logical operators:",
File diff suppressed because it is too large Load Diff
+5 -2
View File
@@ -410,6 +410,8 @@
"title": "Filters",
"clear": "Clear Filters",
"showResults": "Show results",
"fromView": "The current view has a filter set as well:",
"fromViewBoth": "It will be used in combination with what you enter here.",
"attributes": {
"title": "Title",
"titlePlaceholder": "The saved filter title goes here…",
@@ -447,7 +449,7 @@
"help": {
"intro": "To filter tasks, you can use a query syntax similar to SQL. The available fields for filtering include:",
"link": "How does this work?",
"canUseDatemath": "You can date math to set relative dates. Click on the date value in a query to find out more.",
"canUseDatemath": "You can use date math to set relative dates. Click on the date value in a query to find out more.",
"fields": {
"done": "Whether the task is completed or not",
"priority": "The priority level of the task (1-5)",
@@ -472,7 +474,8 @@
"lessThan": "Less than",
"lessThanOrEqual": "Less than or equal to",
"like": "Matches a pattern (using wildcard %)",
"in": "Matches any value in a comma-seperated list of values"
"in": "Matches any value in a comma-seperated list of values",
"notIn": "Matches any value not present in a comma-seperated list of values"
},
"logicalOperators": {
"intro": "To combine multiple conditions, you can use the following logical operators:",
+5 -2
View File
@@ -410,6 +410,8 @@
"title": "Filtry",
"clear": "Vymazat filtry",
"showResults": "Zobrazit výsledky",
"fromView": "The current view has a filter set as well:",
"fromViewBoth": "It will be used in combination with what you enter here.",
"attributes": {
"title": "Název",
"titlePlaceholder": "Název uloženého filtru přijde sem…",
@@ -447,7 +449,7 @@
"help": {
"intro": "K filtrování úkolů můžete použít syntaxi dotazů podobnou SQL. Dostupná pole pro filtrování zahrnují:",
"link": "Jak to funguje?",
"canUseDatemath": "Můžete použít matematiku a nastavit relativní data. Pro více informací klikněte na datum v dotazu.",
"canUseDatemath": "You can use date math to set relative dates. Click on the date value in a query to find out more.",
"fields": {
"done": "Zda je úkol dokončen nebo ne",
"priority": "Úroveň priority úkolu (1-5)",
@@ -472,7 +474,8 @@
"lessThan": "Menší než",
"lessThanOrEqual": "Menší nebo rovno než",
"like": "Odpovídá vzoru (s použitím zástupného znaku %)",
"in": "Odpovídá libovolné hodnotě v seznamu hodnot oddělených čárkou"
"in": "Odpovídá libovolné hodnotě v seznamu hodnot oddělených čárkou",
"notIn": "Matches any value not present in a comma-seperated list of values"
},
"logicalOperators": {
"intro": "Pro kombinování více podmínek můžete použít tyto logické operátory:",
+5 -2
View File
@@ -410,6 +410,8 @@
"title": "Filtre",
"clear": "Ryd Filtre",
"showResults": "Show results",
"fromView": "The current view has a filter set as well:",
"fromViewBoth": "It will be used in combination with what you enter here.",
"attributes": {
"title": "Titel",
"titlePlaceholder": "Det gemte filters titel skrives her…",
@@ -447,7 +449,7 @@
"help": {
"intro": "To filter tasks, you can use a query syntax similar to SQL. The available fields for filtering include:",
"link": "How does this work?",
"canUseDatemath": "You can date math to set relative dates. Click on the date value in a query to find out more.",
"canUseDatemath": "You can use date math to set relative dates. Click on the date value in a query to find out more.",
"fields": {
"done": "Whether the task is completed or not",
"priority": "The priority level of the task (1-5)",
@@ -472,7 +474,8 @@
"lessThan": "Less than",
"lessThanOrEqual": "Less than or equal to",
"like": "Matches a pattern (using wildcard %)",
"in": "Matches any value in a comma-seperated list of values"
"in": "Matches any value in a comma-seperated list of values",
"notIn": "Matches any value not present in a comma-seperated list of values"
},
"logicalOperators": {
"intro": "To combine multiple conditions, you can use the following logical operators:",
+4 -1
View File
@@ -410,6 +410,8 @@
"title": "Filter",
"clear": "Filter zurücksetzen",
"showResults": "Ergebnisse anzeigen",
"fromView": "Die aktuelle Ansicht hat bereits einen Filter gesetzt:",
"fromViewBoth": "Er wird in Kombination mit dem, was du hier setzt, verwendet.",
"attributes": {
"title": "Titel",
"titlePlaceholder": "Einen gespeicherten Filternamen eingeben …",
@@ -472,7 +474,8 @@
"lessThan": "Kleiner als",
"lessThanOrEqual": "Kleiner als oder gleich",
"like": "Vergleicht zu einem Muster (mit Platzhalter %)",
"in": "Filtert einen beliebigen Wert in einer kommaseparierten Liste"
"in": "Filtert einen beliebigen Wert in einer kommaseparierten Liste",
"notIn": "Filtert einen beliebigen Wert, der nicht in einer kommaseparierten vorkommt"
},
"logicalOperators": {
"intro": "Um mehrere Bedingungen zu kombinieren, kannst du folgende logische Operatoren verwenden:",
+4 -1
View File
@@ -410,6 +410,8 @@
"title": "Filter",
"clear": "Filter zurücksetzen",
"showResults": "Ergebnisse anzeigen",
"fromView": "Die aktuelle Ansicht hat bereits einen Filter gesetzt:",
"fromViewBoth": "Er wird in Kombination mit dem, was du hier setzt, verwendet.",
"attributes": {
"title": "Titl",
"titlePlaceholder": "De Name für de g'speicheret Filter chunt da ahne…",
@@ -472,7 +474,8 @@
"lessThan": "Kleiner als",
"lessThanOrEqual": "Kleiner als oder gleich",
"like": "Vergleicht zu einem Muster (mit Platzhalter %)",
"in": "Filtert einen beliebigen Wert in einer kommaseparierten Liste"
"in": "Filtert einen beliebigen Wert in einer kommaseparierten Liste",
"notIn": "Filtert einen beliebigen Wert, der nicht in einer kommaseparierten vorkommt"
},
"logicalOperators": {
"intro": "Um mehrere Bedingungen zu kombinieren, kannst du folgende logische Operatoren verwenden:",
+5 -2
View File
@@ -410,6 +410,8 @@
"title": "Filters",
"clear": "Clear Filters",
"showResults": "Show results",
"fromView": "The current view has a filter set as well:",
"fromViewBoth": "It will be used in combination with what you enter here.",
"attributes": {
"title": "Title",
"titlePlaceholder": "The saved filter title goes here…",
@@ -447,7 +449,7 @@
"help": {
"intro": "To filter tasks, you can use a query syntax similar to SQL. The available fields for filtering include:",
"link": "How does this work?",
"canUseDatemath": "You can date math to set relative dates. Click on the date value in a query to find out more.",
"canUseDatemath": "You can use date math to set relative dates. Click on the date value in a query to find out more.",
"fields": {
"done": "Whether the task is completed or not",
"priority": "The priority level of the task (1-5)",
@@ -472,7 +474,8 @@
"lessThan": "Less than",
"lessThanOrEqual": "Less than or equal to",
"like": "Matches a pattern (using wildcard %)",
"in": "Matches any value in a comma-seperated list of values"
"in": "Matches any value in a comma-seperated list of values",
"notIn": "Matches any value not present in a comma-seperated list of values"
},
"logicalOperators": {
"intro": "To combine multiple conditions, you can use the following logical operators:",
+5 -2
View File
@@ -410,6 +410,8 @@
"title": "Filters",
"clear": "Clear Filters",
"showResults": "Show results",
"fromView": "The current view has a filter set as well:",
"fromViewBoth": "It will be used in combination with what you enter here.",
"attributes": {
"title": "Title",
"titlePlaceholder": "The saved filter title goes here…",
@@ -447,7 +449,7 @@
"help": {
"intro": "To filter tasks, you can use a query syntax similar to SQL. The available fields for filtering include:",
"link": "How does this work?",
"canUseDatemath": "You can date math to set relative dates. Click on the date value in a query to find out more.",
"canUseDatemath": "You can use date math to set relative dates. Click on the date value in a query to find out more.",
"fields": {
"done": "Whether the task is completed or not",
"priority": "The priority level of the task (1-5)",
@@ -472,7 +474,8 @@
"lessThan": "Less than",
"lessThanOrEqual": "Less than or equal to",
"like": "Matches a pattern (using wildcard %)",
"in": "Matches any value in a comma-seperated list of values"
"in": "Matches any value in a comma-seperated list of values",
"notIn": "Matches any value not present in a comma-seperated list of values"
},
"logicalOperators": {
"intro": "To combine multiple conditions, you can use the following logical operators:",
+5 -2
View File
@@ -410,6 +410,8 @@
"title": "Filtros",
"clear": "Limpiar Filtros",
"showResults": "Show results",
"fromView": "The current view has a filter set as well:",
"fromViewBoth": "It will be used in combination with what you enter here.",
"attributes": {
"title": "Título",
"titlePlaceholder": "El título del filtro guardado va aquí…",
@@ -447,7 +449,7 @@
"help": {
"intro": "To filter tasks, you can use a query syntax similar to SQL. The available fields for filtering include:",
"link": "How does this work?",
"canUseDatemath": "You can date math to set relative dates. Click on the date value in a query to find out more.",
"canUseDatemath": "You can use date math to set relative dates. Click on the date value in a query to find out more.",
"fields": {
"done": "Whether the task is completed or not",
"priority": "The priority level of the task (1-5)",
@@ -472,7 +474,8 @@
"lessThan": "Less than",
"lessThanOrEqual": "Less than or equal to",
"like": "Matches a pattern (using wildcard %)",
"in": "Matches any value in a comma-seperated list of values"
"in": "Matches any value in a comma-seperated list of values",
"notIn": "Matches any value not present in a comma-seperated list of values"
},
"logicalOperators": {
"intro": "To combine multiple conditions, you can use the following logical operators:",
+5 -2
View File
@@ -410,6 +410,8 @@
"title": "Filtres",
"clear": "Effacer les filtres",
"showResults": "Show results",
"fromView": "The current view has a filter set as well:",
"fromViewBoth": "It will be used in combination with what you enter here.",
"attributes": {
"title": "Nom",
"titlePlaceholder": "Entre un nom de filtre enregistré…",
@@ -447,7 +449,7 @@
"help": {
"intro": "To filter tasks, you can use a query syntax similar to SQL. The available fields for filtering include:",
"link": "How does this work?",
"canUseDatemath": "You can date math to set relative dates. Click on the date value in a query to find out more.",
"canUseDatemath": "You can use date math to set relative dates. Click on the date value in a query to find out more.",
"fields": {
"done": "Whether the task is completed or not",
"priority": "The priority level of the task (1-5)",
@@ -472,7 +474,8 @@
"lessThan": "Less than",
"lessThanOrEqual": "Less than or equal to",
"like": "Matches a pattern (using wildcard %)",
"in": "Matches any value in a comma-seperated list of values"
"in": "Matches any value in a comma-seperated list of values",
"notIn": "Matches any value not present in a comma-seperated list of values"
},
"logicalOperators": {
"intro": "To combine multiple conditions, you can use the following logical operators:",
+5 -2
View File
@@ -410,6 +410,8 @@
"title": "Filteri",
"clear": "Poništi Filtere",
"showResults": "Prikaži rezultate",
"fromView": "The current view has a filter set as well:",
"fromViewBoth": "It will be used in combination with what you enter here.",
"attributes": {
"title": "Naslov",
"titlePlaceholder": "Naslov spremljenog filtra ide ovdje…",
@@ -447,7 +449,7 @@
"help": {
"intro": "Za filtriranje zadataka možete koristiti sintaksu upita sličnu SQL-u. Dostupna polja za filtriranje uključuju:",
"link": "Kako ovo radi?",
"canUseDatemath": "Možete datirati matematiku da postavite relativne datume. Kliknite vrijednost datuma u upitu kako biste saznali više.",
"canUseDatemath": "You can use date math to set relative dates. Click on the date value in a query to find out more.",
"fields": {
"done": "Whether the task is completed or not",
"priority": "The priority level of the task (1-5)",
@@ -472,7 +474,8 @@
"lessThan": "Less than",
"lessThanOrEqual": "Less than or equal to",
"like": "Matches a pattern (using wildcard %)",
"in": "Matches any value in a comma-seperated list of values"
"in": "Matches any value in a comma-seperated list of values",
"notIn": "Matches any value not present in a comma-seperated list of values"
},
"logicalOperators": {
"intro": "To combine multiple conditions, you can use the following logical operators:",
+5 -2
View File
@@ -410,6 +410,8 @@
"title": "Szűrők",
"clear": "Szűrők törlése",
"showResults": "Eredmények megjelenítése",
"fromView": "The current view has a filter set as well:",
"fromViewBoth": "It will be used in combination with what you enter here.",
"attributes": {
"title": "Cím",
"titlePlaceholder": "A mentett szűrő címe ide kerül…",
@@ -447,7 +449,7 @@
"help": {
"intro": "A feladatok szűréséhez az SQL-hez hasonló lekérdezési szintaxist használhat. A szűréshez rendelkezésre álló mezők a következők:",
"link": "Hogyan működik?",
"canUseDatemath": "A relatív dátumok beállításához a matematikai dátumokat is használhat. További információért kattintson a dátum értékére a lekérdezésben.",
"canUseDatemath": "You can use date math to set relative dates. Click on the date value in a query to find out more.",
"fields": {
"done": "Akár befejeződött a feladat, akár nem",
"priority": "A feladat prioritási szintje (1-5)",
@@ -472,7 +474,8 @@
"lessThan": "Kevesebb, mint",
"lessThanOrEqual": "Kevesebb, vagy egyenlő mint",
"like": "Megfelel egy mintának (helyettesítő karakterrel %)",
"in": "A vesszővel elválasztott értéklistában szereplő bármely értéknek megfelel"
"in": "A vesszővel elválasztott értéklistában szereplő bármely értéknek megfelel",
"notIn": "Matches any value not present in a comma-seperated list of values"
},
"logicalOperators": {
"intro": "Több feltétel kombinálásához a következő logikai operátorokat használhatja:",
+5 -2
View File
@@ -410,6 +410,8 @@
"title": "Filtri",
"clear": "Pulisci Filtri",
"showResults": "Show results",
"fromView": "The current view has a filter set as well:",
"fromViewBoth": "It will be used in combination with what you enter here.",
"attributes": {
"title": "Titolo",
"titlePlaceholder": "Il titolo del filtro salvato va qui…",
@@ -447,7 +449,7 @@
"help": {
"intro": "To filter tasks, you can use a query syntax similar to SQL. The available fields for filtering include:",
"link": "How does this work?",
"canUseDatemath": "You can date math to set relative dates. Click on the date value in a query to find out more.",
"canUseDatemath": "You can use date math to set relative dates. Click on the date value in a query to find out more.",
"fields": {
"done": "Whether the task is completed or not",
"priority": "The priority level of the task (1-5)",
@@ -472,7 +474,8 @@
"lessThan": "Less than",
"lessThanOrEqual": "Less than or equal to",
"like": "Matches a pattern (using wildcard %)",
"in": "Matches any value in a comma-seperated list of values"
"in": "Matches any value in a comma-seperated list of values",
"notIn": "Matches any value not present in a comma-seperated list of values"
},
"logicalOperators": {
"intro": "To combine multiple conditions, you can use the following logical operators:",
+5 -2
View File
@@ -410,6 +410,8 @@
"title": "絞り込み",
"clear": "絞り込みの解除",
"showResults": "Show results",
"fromView": "The current view has a filter set as well:",
"fromViewBoth": "It will be used in combination with what you enter here.",
"attributes": {
"title": "絞り込み条件名",
"titlePlaceholder": "絞り込み条件名を入力…",
@@ -447,7 +449,7 @@
"help": {
"intro": "To filter tasks, you can use a query syntax similar to SQL. The available fields for filtering include:",
"link": "How does this work?",
"canUseDatemath": "You can date math to set relative dates. Click on the date value in a query to find out more.",
"canUseDatemath": "You can use date math to set relative dates. Click on the date value in a query to find out more.",
"fields": {
"done": "Whether the task is completed or not",
"priority": "The priority level of the task (1-5)",
@@ -472,7 +474,8 @@
"lessThan": "Less than",
"lessThanOrEqual": "Less than or equal to",
"like": "Matches a pattern (using wildcard %)",
"in": "Matches any value in a comma-seperated list of values"
"in": "Matches any value in a comma-seperated list of values",
"notIn": "Matches any value not present in a comma-seperated list of values"
},
"logicalOperators": {
"intro": "To combine multiple conditions, you can use the following logical operators:",
+5 -2
View File
@@ -410,6 +410,8 @@
"title": "필터",
"clear": "필터 초기화",
"showResults": "Show results",
"fromView": "The current view has a filter set as well:",
"fromViewBoth": "It will be used in combination with what you enter here.",
"attributes": {
"title": "제목",
"titlePlaceholder": "The saved filter title goes here…",
@@ -447,7 +449,7 @@
"help": {
"intro": "To filter tasks, you can use a query syntax similar to SQL. The available fields for filtering include:",
"link": "How does this work?",
"canUseDatemath": "You can date math to set relative dates. Click on the date value in a query to find out more.",
"canUseDatemath": "You can use date math to set relative dates. Click on the date value in a query to find out more.",
"fields": {
"done": "Whether the task is completed or not",
"priority": "The priority level of the task (1-5)",
@@ -472,7 +474,8 @@
"lessThan": "Less than",
"lessThanOrEqual": "Less than or equal to",
"like": "Matches a pattern (using wildcard %)",
"in": "Matches any value in a comma-seperated list of values"
"in": "Matches any value in a comma-seperated list of values",
"notIn": "Matches any value not present in a comma-seperated list of values"
},
"logicalOperators": {
"intro": "To combine multiple conditions, you can use the following logical operators:",
+5 -2
View File
@@ -410,6 +410,8 @@
"title": "Filtrai",
"clear": "Anuliuoti filtrus",
"showResults": "Rodyti rezultatus",
"fromView": "The current view has a filter set as well:",
"fromViewBoth": "It will be used in combination with what you enter here.",
"attributes": {
"title": "Pavadinimas",
"titlePlaceholder": "Išsaugoto filtro pavadinimas rašomas čia…",
@@ -447,7 +449,7 @@
"help": {
"intro": "Norėdami filtruoti užduotis, galite naudoti sintaksę panašią į SQL. Galimi filtravimo laukai įtraukia:",
"link": "Kaip tai veikia?",
"canUseDatemath": "You can date math to set relative dates. Click on the date value in a query to find out more.",
"canUseDatemath": "You can use date math to set relative dates. Click on the date value in a query to find out more.",
"fields": {
"done": "Ar užduotis atlikta ar ne",
"priority": "Užduoties prioriteto lygis (1-5)",
@@ -472,7 +474,8 @@
"lessThan": "Mažiau nei",
"lessThanOrEqual": "Mažiau negu ar lygu",
"like": "Atitinka šabloną (nadojama wildcard %)",
"in": "Matches any value in a comma-seperated list of values"
"in": "Matches any value in a comma-seperated list of values",
"notIn": "Matches any value not present in a comma-seperated list of values"
},
"logicalOperators": {
"intro": "To combine multiple conditions, you can use the following logical operators:",
+5 -2
View File
@@ -410,6 +410,8 @@
"title": "Filters",
"clear": "Clear Filters",
"showResults": "Show results",
"fromView": "The current view has a filter set as well:",
"fromViewBoth": "It will be used in combination with what you enter here.",
"attributes": {
"title": "Titel",
"titlePlaceholder": "The saved filter title goes here…",
@@ -447,7 +449,7 @@
"help": {
"intro": "To filter tasks, you can use a query syntax similar to SQL. The available fields for filtering include:",
"link": "How does this work?",
"canUseDatemath": "You can date math to set relative dates. Click on the date value in a query to find out more.",
"canUseDatemath": "You can use date math to set relative dates. Click on the date value in a query to find out more.",
"fields": {
"done": "Whether the task is completed or not",
"priority": "The priority level of the task (1-5)",
@@ -472,7 +474,8 @@
"lessThan": "Less than",
"lessThanOrEqual": "Less than or equal to",
"like": "Matches a pattern (using wildcard %)",
"in": "Matches any value in a comma-seperated list of values"
"in": "Matches any value in a comma-seperated list of values",
"notIn": "Matches any value not present in a comma-seperated list of values"
},
"logicalOperators": {
"intro": "To combine multiple conditions, you can use the following logical operators:",
+5 -2
View File
@@ -410,6 +410,8 @@
"title": "Filtre",
"clear": "Fjern filtre",
"showResults": "Show results",
"fromView": "The current view has a filter set as well:",
"fromViewBoth": "It will be used in combination with what you enter here.",
"attributes": {
"title": "Tittel",
"titlePlaceholder": "Den lagrede filtertittelen kommer hit…",
@@ -447,7 +449,7 @@
"help": {
"intro": "To filter tasks, you can use a query syntax similar to SQL. The available fields for filtering include:",
"link": "How does this work?",
"canUseDatemath": "You can date math to set relative dates. Click on the date value in a query to find out more.",
"canUseDatemath": "You can use date math to set relative dates. Click on the date value in a query to find out more.",
"fields": {
"done": "Whether the task is completed or not",
"priority": "The priority level of the task (1-5)",
@@ -472,7 +474,8 @@
"lessThan": "Less than",
"lessThanOrEqual": "Less than or equal to",
"like": "Matches a pattern (using wildcard %)",
"in": "Matches any value in a comma-seperated list of values"
"in": "Matches any value in a comma-seperated list of values",
"notIn": "Matches any value not present in a comma-seperated list of values"
},
"logicalOperators": {
"intro": "To combine multiple conditions, you can use the following logical operators:",
+5 -2
View File
@@ -410,6 +410,8 @@
"title": "Filtry",
"clear": "Wyczyść filtry",
"showResults": "Wyświetl wyniki",
"fromView": "The current view has a filter set as well:",
"fromViewBoth": "It will be used in combination with what you enter here.",
"attributes": {
"title": "Tytuł",
"titlePlaceholder": "Tu wpisz tytuł filtra stałego…",
@@ -447,7 +449,7 @@
"help": {
"intro": "Aby filtrować zadania, możesz użyć składni zapytań podobnej do SQL. Pola dostępne do filtrowania to:",
"link": "Jak to działa?",
"canUseDatemath": "Możesz użyć matematyki dat, aby ustawić daty względne. Kliknij wartość daty w zapytaniu, aby dowiedzieć się więcej.",
"canUseDatemath": "You can use date math to set relative dates. Click on the date value in a query to find out more.",
"fields": {
"done": "Czy zadanie zostało zakończone, czy nie",
"priority": "Priorytet zadania (1-5)",
@@ -472,7 +474,8 @@
"lessThan": "Mniejsze niż",
"lessThanOrEqual": "Mniejsze niż lub równe",
"like": "Pasuje do wzorca (używając symbolu %)",
"in": "Dopasuje dowolną wartość z listy wartości oddzielonych przecinkami"
"in": "Dopasuje dowolną wartość z listy wartości oddzielonych przecinkami",
"notIn": "Matches any value not present in a comma-seperated list of values"
},
"logicalOperators": {
"intro": "Aby połączyć wiele warunków, możesz użyć następujących operatorów logicznych:",
+5 -2
View File
@@ -410,6 +410,8 @@
"title": "Filtros",
"clear": "Limpar Filtros",
"showResults": "Show results",
"fromView": "The current view has a filter set as well:",
"fromViewBoth": "It will be used in combination with what you enter here.",
"attributes": {
"title": "Título",
"titlePlaceholder": "O título do filtro salvo fica aqui…",
@@ -447,7 +449,7 @@
"help": {
"intro": "To filter tasks, you can use a query syntax similar to SQL. The available fields for filtering include:",
"link": "How does this work?",
"canUseDatemath": "You can date math to set relative dates. Click on the date value in a query to find out more.",
"canUseDatemath": "You can use date math to set relative dates. Click on the date value in a query to find out more.",
"fields": {
"done": "Whether the task is completed or not",
"priority": "The priority level of the task (1-5)",
@@ -472,7 +474,8 @@
"lessThan": "Less than",
"lessThanOrEqual": "Less than or equal to",
"like": "Matches a pattern (using wildcard %)",
"in": "Matches any value in a comma-seperated list of values"
"in": "Matches any value in a comma-seperated list of values",
"notIn": "Matches any value not present in a comma-seperated list of values"
},
"logicalOperators": {
"intro": "To combine multiple conditions, you can use the following logical operators:",
+5 -2
View File
@@ -410,6 +410,8 @@
"title": "Filtros",
"clear": "Limpar Filtros",
"showResults": "Show results",
"fromView": "The current view has a filter set as well:",
"fromViewBoth": "It will be used in combination with what you enter here.",
"attributes": {
"title": "Título",
"titlePlaceholder": "O título do filtro memorizado será aqui…",
@@ -447,7 +449,7 @@
"help": {
"intro": "To filter tasks, you can use a query syntax similar to SQL. The available fields for filtering include:",
"link": "How does this work?",
"canUseDatemath": "You can date math to set relative dates. Click on the date value in a query to find out more.",
"canUseDatemath": "You can use date math to set relative dates. Click on the date value in a query to find out more.",
"fields": {
"done": "Whether the task is completed or not",
"priority": "The priority level of the task (1-5)",
@@ -472,7 +474,8 @@
"lessThan": "Less than",
"lessThanOrEqual": "Less than or equal to",
"like": "Matches a pattern (using wildcard %)",
"in": "Matches any value in a comma-seperated list of values"
"in": "Matches any value in a comma-seperated list of values",
"notIn": "Matches any value not present in a comma-seperated list of values"
},
"logicalOperators": {
"intro": "To combine multiple conditions, you can use the following logical operators:",
+5 -2
View File
@@ -410,6 +410,8 @@
"title": "Filters",
"clear": "Clear Filters",
"showResults": "Show results",
"fromView": "The current view has a filter set as well:",
"fromViewBoth": "It will be used in combination with what you enter here.",
"attributes": {
"title": "Title",
"titlePlaceholder": "The saved filter title goes here…",
@@ -447,7 +449,7 @@
"help": {
"intro": "To filter tasks, you can use a query syntax similar to SQL. The available fields for filtering include:",
"link": "How does this work?",
"canUseDatemath": "You can date math to set relative dates. Click on the date value in a query to find out more.",
"canUseDatemath": "You can use date math to set relative dates. Click on the date value in a query to find out more.",
"fields": {
"done": "Whether the task is completed or not",
"priority": "The priority level of the task (1-5)",
@@ -472,7 +474,8 @@
"lessThan": "Less than",
"lessThanOrEqual": "Less than or equal to",
"like": "Matches a pattern (using wildcard %)",
"in": "Matches any value in a comma-seperated list of values"
"in": "Matches any value in a comma-seperated list of values",
"notIn": "Matches any value not present in a comma-seperated list of values"
},
"logicalOperators": {
"intro": "To combine multiple conditions, you can use the following logical operators:",
+32 -29
View File
@@ -43,7 +43,7 @@
"forgotPassword": "Забыли пароль?",
"resetPassword": "Сбросить пароль",
"resetPasswordAction": "Отправить ссылку на сброс пароля",
"resetPasswordSuccess": "Check your inbox! You should have an email with instructions on how to reset your password.",
"resetPasswordSuccess": "Проверь свою почту! Там должно быть письмо с инструкциями, как сбросить пароль.",
"passwordsDontMatch": "Пароли не совпадают",
"confirmEmailSuccess": "Теперь вы можете войти, используя свой e-mail.",
"totpTitle": "Код двухфакторной аутентификации",
@@ -61,7 +61,7 @@
"usernameMustNotLookLikeUrl": "Имя пользователя не должно быть похожим на URL.",
"passwordRequired": "Введите пароль.",
"passwordNotMin": "Пароль должен содержать не меньше 8 символов.",
"passwordNotMax": "Password must have at most 72 characters.",
"passwordNotMax": "Пароль должен содержать не больше 72 символов.",
"showPassword": "Показать пароль",
"hidePassword": "Скрыть пароль",
"noAccountYet": "Ещё нет аккаунта?",
@@ -72,21 +72,21 @@
"settings": {
"title": "Настройки",
"newPasswordTitle": "Изменить пароль",
"newPassword": "New password",
"newPasswordConfirm": "New password confirmation",
"currentPassword": "Current password",
"newPassword": "Новый пароль",
"newPasswordConfirm": "Новый пароль ещё раз",
"currentPassword": "Текущий пароль",
"currentPasswordPlaceholder": "Текущий пароль",
"passwordsDontMatch": "Новые пароли не совпадают.",
"passwordUpdateSuccess": "Пароль изменён.",
"updateEmailTitle": "Изменить Email",
"updateEmailNew": "New email address",
"updateEmailNew": "Новый email адрес",
"updateEmailSuccess": "E-mail успешно изменён. Для подтверждения нажмите на ссылку в письме, отправленном на новый адрес.",
"general": {
"title": "Основные настройки",
"name": "My Name",
"newName": "The new name",
"name": "Моё имя",
"newName": "Новое имя",
"savedSuccess": "Настройки обновлены.",
"emailReminders": "Send me reminders for tasks via email",
"emailReminders": "Присылать мне напоминания о задачах на email",
"overdueReminders": "Присылать сводку о просроченных задачах каждый день",
"discoverableByName": "Разрешить другим пользователям добавлять меня в состав команд или проектов при поиске моего имени",
"discoverableByEmail": "Разрешить другим пользователям добавлять меня в состав команд или проектов при поиске моего полного email",
@@ -95,24 +95,24 @@
"weekStartSunday": "Воскресенье",
"weekStartMonday": "Понедельник",
"language": "Язык",
"defaultProject": "Default project",
"defaultView": "Default view",
"timezone": "Time zone",
"defaultProject": "Проект по умолчанию",
"defaultView": "Представление по умолчанию",
"timezone": "Часовой пояс",
"overdueTasksRemindersTime": "Время напоминания о невыполненных задачах",
"filterUsedOnOverview": "Сохранённый фильтр, используемый на странице обзора"
},
"totp": {
"title": "Двухфакторная аутентификация",
"enroll": "Подключить",
"finishSetupPart1": "To finish your setup, use this secret in your TOTP app (Google Authenticator or similar):",
"finishSetupPart1": "Для завершения подключения используйте этот секрет в своём приложении TOTP (Google Authenticator или подобное):",
"finishSetupPart2": "Потом введите сюда код из приложения.",
"scanQR": "Или вы можете отсканировать этот QR-код:",
"passcode": "Код",
"passcodePlaceholder": "A code generated by your TOTP application",
"passcodePlaceholder": "Код, который сгенерировало приложение TOTP",
"setupSuccess": "Двухфакторная аутентификация успешно подключена!",
"enterPassword": "Введите свой пароль",
"disable": "Отключить двухфакторную аутентификацию",
"confirmSuccess": "You've successfully confirmed your TOTP setup and can use it from now on!",
"confirmSuccess": "TOTP настроен и готов к использованию!",
"disableSuccess": "Двухфакторная аутентификация отключена."
},
"caldav": {
@@ -163,7 +163,7 @@
"90d": "90 дней",
"permissionExplanation": "Разрешения позволяют выбрать, какие действия можно выполнять с использованием этого токена.",
"titleRequired": "Название обязательно",
"permissionRequired": "Please select at least one permission from the list.",
"permissionRequired": "Выберите хотя бы одно разрешение из списка.",
"expired": "Срок действия этого токена истёк {ago}.",
"tokenCreatedSuccess": "Ваш новый токен: {token}",
"tokenCreatedNotSeeAgain": "Сохраните его в безопасном месте, вы не увидите его снова!",
@@ -401,15 +401,17 @@
"titleRequired": "Пожалуйста, укажите название.",
"delete": "Удалить представление",
"deleteText": "Удалить это представление? После удаления его не получится использовать для просмотра задач в этом проекте. Сами задачи останутся. Это действие отменить нельзя!",
"deleteSuccess": "The view was deleted successfully.",
"deleteSuccess": "Представление удалено.",
"onlyAdminsCanEdit": "Только администраторы проекта могут изменять представления.",
"updateSuccess": "The view was updated successfully."
"updateSuccess": "Представление обновлено."
}
},
"filters": {
"title": "Фильтры",
"clear": "Сбросить фильтры",
"showResults": "Применить",
"fromView": "В текущем представлении тоже указан фильтр:",
"fromViewBoth": "Он будет использоваться вместе с фильтром, который вы введёте здесь.",
"attributes": {
"title": "Название",
"titlePlaceholder": "Введите название сохранённого фильтра…",
@@ -459,9 +461,9 @@
"assignees": "Пользователи, которым назначена задача",
"labels": "Метки, связанные с задачей",
"project": "Проект, в котором находится задача (только для сохранённых фильтров, не на уровне проекта)",
"reminders": "The reminders of the task as a date field, will return all tasks with at least one reminder matching the query",
"created": "The time and date when the task was created",
"updated": "The time and date when the task was last changed"
"reminders": "Напоминания задачи в виде даты, выведет все задачи, в которых найдётся хотя бы одно напоминание, соответствующее запросу",
"created": "Время и дата создания задачи",
"updated": "Время и дата последнего изменения задачи"
},
"operators": {
"intro": "Доступны следующие операторы:",
@@ -472,7 +474,8 @@
"lessThan": "Меньше",
"lessThanOrEqual": "Меньше или равно",
"like": "Соответствует шаблону (знак подстановки %)",
"in": "Соответствует любому из перечисленных значений, разделённых запятой"
"in": "Соответствует любому из перечисленных значений, разделённых запятой",
"notIn": "Соответствует чему угодно, кроме перечисленных значений, разделённых запятой"
},
"logicalOperators": {
"intro": "Для объединения нескольких условий можно использовать логические операторы:",
@@ -736,7 +739,7 @@
"addReminder": "Добавить напоминание…",
"doneSuccess": "Задача отмечена как завершённая.",
"undoneSuccess": "Задача отмечена как незавершённая.",
"revertSuccess": "The task change was successfully undone.",
"revertSuccess": "Изменение задачи отменено.",
"undo": "Отменить",
"openDetail": "Открыть подробный просмотр задачи",
"checklistTotal": "{checked} из {total} задач",
@@ -852,7 +855,7 @@
"deleteText1": "Удалить этот комментарий?",
"deleteSuccess": "Комментарий удалён.",
"addedSuccess": "Комментарий добавлен.",
"permalink": "Copy permalink to this comment"
"permalink": "Скопировать постоянную ссылку на комментарий"
},
"deferDueDate": {
"title": "Отложить срок",
@@ -1146,9 +1149,9 @@
"1012": "E-mail пользователя не подтверждён.",
"1013": "Новый пароль пустой.",
"1014": "Старый пароль пустой.",
"1015": "TOTP is already enabled for this user.",
"1016": "TOTP is not enabled for this user.",
"1017": "The TOTP passcode is invalid.",
"1015": "Этот пользователь уже использует TOTP.",
"1016": "Этот пользователь не использует TOTP.",
"1017": "Неверный TOTP-код.",
"1018": "Тип аватара пользователя неверный.",
"2001": "ID не может быть пустой или 0.",
"2002": "Некоторые данные запроса неверны.",
@@ -1158,8 +1161,8 @@
"3006": "The project share does not exist.",
"3007": "Проект с таким идентификатором уже существует.",
"3008": "Этот проект архивирован и поэтому доступен только для чтения. Это также касается всех задач в этом проекте.",
"4001": "The task title cannot be empty.",
"4002": "The task does not exist.",
"4001": "Нужно ввести название задачи.",
"4002": "Задача не существует.",
"4003": "Все задачи для массового редактирования должны принадлежать одному проекту.",
"4004": "Необходима хотя бы одна задача для массового редактирования.",
"4005": "У вас нет прав для просмотра задачи.",
+5 -2
View File
@@ -410,6 +410,8 @@
"title": "Filters",
"clear": "Clear Filters",
"showResults": "Show results",
"fromView": "The current view has a filter set as well:",
"fromViewBoth": "It will be used in combination with what you enter here.",
"attributes": {
"title": "Title",
"titlePlaceholder": "The saved filter title goes here…",
@@ -447,7 +449,7 @@
"help": {
"intro": "To filter tasks, you can use a query syntax similar to SQL. The available fields for filtering include:",
"link": "How does this work?",
"canUseDatemath": "You can date math to set relative dates. Click on the date value in a query to find out more.",
"canUseDatemath": "You can use date math to set relative dates. Click on the date value in a query to find out more.",
"fields": {
"done": "Whether the task is completed or not",
"priority": "The priority level of the task (1-5)",
@@ -472,7 +474,8 @@
"lessThan": "Less than",
"lessThanOrEqual": "Less than or equal to",
"like": "Matches a pattern (using wildcard %)",
"in": "Matches any value in a comma-seperated list of values"
"in": "Matches any value in a comma-seperated list of values",
"notIn": "Matches any value not present in a comma-seperated list of values"
},
"logicalOperators": {
"intro": "To combine multiple conditions, you can use the following logical operators:",
+8 -5
View File
@@ -410,6 +410,8 @@
"title": "Filtri",
"clear": "Počisti filtre",
"showResults": "Prikaži rezultate",
"fromView": "Trenutni pogled ima tudi nastavljen filter:",
"fromViewBoth": "Uporabljeno bo v kombinaciji s tem, kar ste vnesli tukaj.",
"attributes": {
"title": "Naslov",
"titlePlaceholder": "Tu je naslov shranjenega filtra…",
@@ -447,7 +449,7 @@
"help": {
"intro": "Za filtriranje nalog lahko uporabite sintakso poizvedbe, podobno SQL. Razpoložljiva polja za filtriranje vključujejo:",
"link": "Kako to deluje?",
"canUseDatemath": "Za nastavitev relativnih datumov lahko določite datume. Če želite izvedeti več, kliknite vrednost datuma v poizvedbi.",
"canUseDatemath": "Za nastavitev relativnih datumov lahko uporabite datumsko matematiko. Če želite izvedeti več, kliknite vrednost datuma v poizvedbi.",
"fields": {
"done": "Ali je naloga opravljena ali ne",
"priority": "Stopnja prioritete naloge (1-5)",
@@ -459,9 +461,9 @@
"assignees": "Dodeljeni k nalogi",
"labels": "Oznake, povezane z opravilom",
"project": "Projekt, ki mu naloga pripada (na voljo samo za shranjene filtre, ne na ravni projekta)",
"reminders": "The reminders of the task as a date field, will return all tasks with at least one reminder matching the query",
"created": "The time and date when the task was created",
"updated": "The time and date when the task was last changed"
"reminders": "Opomniki za opravilo kot datumsko polje bodo vrnili vsa opravila z vsaj enim opomnikom, ki se ujema s poizvedbo",
"created": "Čas in datum, ko je bilo opravilo ustvarjeno",
"updated": "Čas in datum zadnje spremembe opravila"
},
"operators": {
"intro": "Razpoložljivi operaterji za filtriranje vključujejo:",
@@ -472,7 +474,8 @@
"lessThan": "Manj kot",
"lessThanOrEqual": "Manjše ali enako kot",
"like": "Ujema se z vzorcem (z nadomestnimi znaki %)",
"in": "Ujema se s katero koli vrednostjo na seznamu vrednosti, ločenih z vejico"
"in": "Ujema se s katero koli vrednostjo na seznamu vrednosti, ločenih z vejico",
"notIn": "Ujema se s katero koli vrednostjo na seznamu vrednosti, ločenih z vejico"
},
"logicalOperators": {
"intro": "Če želite združiti več pogojev, lahko uporabite naslednje logične operatorje:",
+5 -2
View File
@@ -410,6 +410,8 @@
"title": "Filters",
"clear": "Clear Filters",
"showResults": "Show results",
"fromView": "The current view has a filter set as well:",
"fromViewBoth": "It will be used in combination with what you enter here.",
"attributes": {
"title": "Title",
"titlePlaceholder": "The saved filter title goes here…",
@@ -447,7 +449,7 @@
"help": {
"intro": "To filter tasks, you can use a query syntax similar to SQL. The available fields for filtering include:",
"link": "How does this work?",
"canUseDatemath": "You can date math to set relative dates. Click on the date value in a query to find out more.",
"canUseDatemath": "You can use date math to set relative dates. Click on the date value in a query to find out more.",
"fields": {
"done": "Whether the task is completed or not",
"priority": "The priority level of the task (1-5)",
@@ -472,7 +474,8 @@
"lessThan": "Less than",
"lessThanOrEqual": "Less than or equal to",
"like": "Matches a pattern (using wildcard %)",
"in": "Matches any value in a comma-seperated list of values"
"in": "Matches any value in a comma-seperated list of values",
"notIn": "Matches any value not present in a comma-seperated list of values"
},
"logicalOperators": {
"intro": "To combine multiple conditions, you can use the following logical operators:",
+5 -2
View File
@@ -410,6 +410,8 @@
"title": "Filter",
"clear": "Rensa filter",
"showResults": "Visa resultat",
"fromView": "The current view has a filter set as well:",
"fromViewBoth": "It will be used in combination with what you enter here.",
"attributes": {
"title": "Titel",
"titlePlaceholder": "The saved filter title goes here…",
@@ -447,7 +449,7 @@
"help": {
"intro": "To filter tasks, you can use a query syntax similar to SQL. The available fields for filtering include:",
"link": "How does this work?",
"canUseDatemath": "You can date math to set relative dates. Click on the date value in a query to find out more.",
"canUseDatemath": "You can use date math to set relative dates. Click on the date value in a query to find out more.",
"fields": {
"done": "Whether the task is completed or not",
"priority": "The priority level of the task (1-5)",
@@ -472,7 +474,8 @@
"lessThan": "Mindre än",
"lessThanOrEqual": "Mindre än eller lika med",
"like": "Matches a pattern (using wildcard %)",
"in": "Matches any value in a comma-seperated list of values"
"in": "Matches any value in a comma-seperated list of values",
"notIn": "Matches any value not present in a comma-seperated list of values"
},
"logicalOperators": {
"intro": "To combine multiple conditions, you can use the following logical operators:",
+5 -2
View File
@@ -410,6 +410,8 @@
"title": "Filters",
"clear": "Clear Filters",
"showResults": "Show results",
"fromView": "The current view has a filter set as well:",
"fromViewBoth": "It will be used in combination with what you enter here.",
"attributes": {
"title": "Title",
"titlePlaceholder": "The saved filter title goes here…",
@@ -447,7 +449,7 @@
"help": {
"intro": "To filter tasks, you can use a query syntax similar to SQL. The available fields for filtering include:",
"link": "How does this work?",
"canUseDatemath": "You can date math to set relative dates. Click on the date value in a query to find out more.",
"canUseDatemath": "You can use date math to set relative dates. Click on the date value in a query to find out more.",
"fields": {
"done": "Whether the task is completed or not",
"priority": "The priority level of the task (1-5)",
@@ -472,7 +474,8 @@
"lessThan": "Less than",
"lessThanOrEqual": "Less than or equal to",
"like": "Matches a pattern (using wildcard %)",
"in": "Matches any value in a comma-seperated list of values"
"in": "Matches any value in a comma-seperated list of values",
"notIn": "Matches any value not present in a comma-seperated list of values"
},
"logicalOperators": {
"intro": "To combine multiple conditions, you can use the following logical operators:",
+5 -2
View File
@@ -410,6 +410,8 @@
"title": "Вибірки",
"clear": "Очистити",
"showResults": "Показати",
"fromView": "The current view has a filter set as well:",
"fromViewBoth": "It will be used in combination with what you enter here.",
"attributes": {
"title": "Заголовок",
"titlePlaceholder": "Заголовок введіть тут…",
@@ -447,7 +449,7 @@
"help": {
"intro": "Для вибірки завдань вживається синтаксис запитів подібний до SQL. Доступні такі поля:",
"link": "Як це діє?",
"canUseDatemath": "Можна вжити математичні вирази для обчислення приблизних днів. Клацніть на значення дати для подробиць.",
"canUseDatemath": "You can use date math to set relative dates. Click on the date value in a query to find out more.",
"fields": {
"done": "Стан: закінчене або ні",
"priority": "Рівень першості (1-5)",
@@ -472,7 +474,8 @@
"lessThan": "Менше ніж",
"lessThanOrEqual": "Менше ніж або дорівнює",
"like": "Збігається зі зразком (вживаючи знак %)",
"in": "Збігається з будь-яким значеннями, розділеними комою"
"in": "Збігається з будь-яким значеннями, розділеними комою",
"notIn": "Matches any value not present in a comma-seperated list of values"
},
"logicalOperators": {
"intro": "Щоб поєднати декілька умов, можна вжити такі закономірні оператори:",
+5 -2
View File
@@ -410,6 +410,8 @@
"title": "Bộ lọc",
"clear": "Xoá các bộ lọc",
"showResults": "Show results",
"fromView": "The current view has a filter set as well:",
"fromViewBoth": "It will be used in combination with what you enter here.",
"attributes": {
"title": "Tiêu đề",
"titlePlaceholder": "Tiêu đề bộ lọc đã lưu ở đây…",
@@ -447,7 +449,7 @@
"help": {
"intro": "To filter tasks, you can use a query syntax similar to SQL. The available fields for filtering include:",
"link": "How does this work?",
"canUseDatemath": "You can date math to set relative dates. Click on the date value in a query to find out more.",
"canUseDatemath": "You can use date math to set relative dates. Click on the date value in a query to find out more.",
"fields": {
"done": "Whether the task is completed or not",
"priority": "The priority level of the task (1-5)",
@@ -472,7 +474,8 @@
"lessThan": "Less than",
"lessThanOrEqual": "Less than or equal to",
"like": "Matches a pattern (using wildcard %)",
"in": "Matches any value in a comma-seperated list of values"
"in": "Matches any value in a comma-seperated list of values",
"notIn": "Matches any value not present in a comma-seperated list of values"
},
"logicalOperators": {
"intro": "To combine multiple conditions, you can use the following logical operators:",
+5 -2
View File
@@ -410,6 +410,8 @@
"title": "筛选器",
"clear": "清除筛选条件",
"showResults": "显示结果",
"fromView": "The current view has a filter set as well:",
"fromViewBoth": "It will be used in combination with what you enter here.",
"attributes": {
"title": "标题",
"titlePlaceholder": "填写筛选器标题",
@@ -447,7 +449,7 @@
"help": {
"intro": "要过滤任务,您可以使用类似于SQL的查询语法。可用的过滤字段包括:",
"link": "这是如何运作的?",
"canUseDatemath": "您可以将数学日期设定为相对日期。点击查询中的日期值来查找更多信息。",
"canUseDatemath": "You can use date math to set relative dates. Click on the date value in a query to find out more.",
"fields": {
"done": "任务是否完成",
"priority": "任务的优先级(1-5)",
@@ -472,7 +474,8 @@
"lessThan": "小于",
"lessThanOrEqual": "小于或等于",
"like": "匹配模式 (使用通配符%)",
"in": "匹配任意一个逗号分隔的值列表"
"in": "匹配任意一个逗号分隔的值列表",
"notIn": "Matches any value not present in a comma-seperated list of values"
},
"logicalOperators": {
"intro": "若要合并多个条件,您可以使用以下逻辑运算符:",
+5 -2
View File
@@ -410,6 +410,8 @@
"title": "Filters",
"clear": "Clear Filters",
"showResults": "Show results",
"fromView": "The current view has a filter set as well:",
"fromViewBoth": "It will be used in combination with what you enter here.",
"attributes": {
"title": "Title",
"titlePlaceholder": "The saved filter title goes here…",
@@ -447,7 +449,7 @@
"help": {
"intro": "To filter tasks, you can use a query syntax similar to SQL. The available fields for filtering include:",
"link": "How does this work?",
"canUseDatemath": "You can date math to set relative dates. Click on the date value in a query to find out more.",
"canUseDatemath": "You can use date math to set relative dates. Click on the date value in a query to find out more.",
"fields": {
"done": "Whether the task is completed or not",
"priority": "The priority level of the task (1-5)",
@@ -472,7 +474,8 @@
"lessThan": "Less than",
"lessThanOrEqual": "Less than or equal to",
"like": "Matches a pattern (using wildcard %)",
"in": "Matches any value in a comma-seperated list of values"
"in": "Matches any value in a comma-seperated list of values",
"notIn": "Matches any value not present in a comma-seperated list of values"
},
"logicalOperators": {
"intro": "To combine multiple conditions, you can use the following logical operators:",
@@ -26,6 +26,7 @@ export const DAYJS_LOCALE_MAPPING = {
'hr-HR': 'hr',
'uk-UA': 'uk',
'lt-LT': 'lt',
'bg-BG': 'bg',
} as Record<SupportedLocale, ISOLanguage>
export const DAYJS_LANGUAGE_IMPORTS = {
@@ -50,6 +51,7 @@ export const DAYJS_LANGUAGE_IMPORTS = {
'pt-br': () => import('dayjs/locale/pt-br'),
'uk-ua': () => import('dayjs/locale/uk'),
'lt-lt': () => import('dayjs/locale/lt'),
'bg-bg': () => import('dayjs/locale/bg'),
} as Record<SupportedLocale, () => Promise<ILocale>>
export function useDayjsLanguageSync(dayjsGlobal: typeof dayjs) {
+2 -2
View File
@@ -5,7 +5,7 @@ import type { IUser } from '@/modelTypes/IUser'
import type { IFile } from '@/modelTypes/IFile'
import type { IAttachment } from '@/modelTypes/IAttachment'
export const SUPPORTED_IMAGE_SUFFIX = ['.jpg', '.png', '.bmp', '.gif']
export const SUPPORTED_IMAGE_SUFFIX = ['.jpeg', '.jpg', '.png', '.bmp', '.gif']
export function canPreview(attachment: IAttachment): boolean {
return SUPPORTED_IMAGE_SUFFIX.some((suffix) => attachment.file.name.toLowerCase().endsWith(suffix))
@@ -26,4 +26,4 @@ export default class AttachmentModel extends AbstractModel<IAttachment> implemen
this.file = new FileModel(this.file)
this.created = new Date(this.created)
}
}
}
+13 -13
View File
@@ -273,7 +273,7 @@ describe('Parse Task Text', () => {
const mar = new Date(2022, 2, 32)
vi.setSystemTime(mar)
const result = parseTaskText(`Lorem Ipsum 31st`)
const result = parseTaskText('Lorem Ipsum 31st')
const expectedDate = new Date(2022, 4, 31)
expect(result.text).toBe('Lorem Ipsum')
@@ -617,7 +617,7 @@ describe('Parse Task Text', () => {
expect(result.labels[1]).toBe('label2')
})
it('should correctly parse labels with spaces in them', () => {
const result = parseTaskText(`Lorem *'label with space' Ipsum`)
const result = parseTaskText('Lorem *\'label with space\' Ipsum')
expect(result.text).toBe('Lorem Ipsum')
expect(result.labels).toHaveLength(1)
@@ -647,25 +647,25 @@ describe('Parse Task Text', () => {
expect(result.project).toBe('project')
})
it('should parse a project with a space in it', () => {
const result = parseTaskText(`Lorem Ipsum +'project with long name'`)
const result = parseTaskText('Lorem Ipsum +\'project with long name\'')
expect(result.text).toBe('Lorem Ipsum')
expect(result.project).toBe('project with long name')
})
it('should parse a project with a space in it and "', () => {
const result = parseTaskText(`Lorem Ipsum +"project with long name"`)
const result = parseTaskText('Lorem Ipsum +"project with long name"')
expect(result.text).toBe('Lorem Ipsum')
expect(result.project).toBe('project with long name')
})
it('should parse only the first project', () => {
const result = parseTaskText(`Lorem Ipsum +project1 +project2 +project3`)
const result = parseTaskText('Lorem Ipsum +project1 +project2 +project3')
expect(result.text).toBe('Lorem Ipsum +project2 +project3')
expect(result.project).toBe('project1')
})
it('should parse a project that\'s called like a date as project', () => {
const result = parseTaskText(`Lorem Ipsum +today`)
const result = parseTaskText('Lorem Ipsum +today')
expect(result.text).toBe('Lorem Ipsum')
expect(result.project).toBe('today')
@@ -681,14 +681,14 @@ describe('Parse Task Text', () => {
expect(result.priority).toBe(PRIORITIES[p])
})
}
it(`should not parse an invalid priority`, () => {
const result = parseTaskText(`Lorem Ipsum !9999`)
it('should not parse an invalid priority', () => {
const result = parseTaskText('Lorem Ipsum !9999')
expect(result.text).toBe('Lorem Ipsum !9999')
expect(result.priority).toBe(null)
})
it(`should not parse an invalid priority but use the first valid one it finds`, () => {
const result = parseTaskText(`Lorem Ipsum !9999 !1`)
it('should not parse an invalid priority but use the first valid one it finds', () => {
const result = parseTaskText('Lorem Ipsum !9999 !1')
expect(result.text).toBe('Lorem Ipsum !9999')
expect(result.priority).toBe(1)
@@ -724,7 +724,7 @@ describe('Parse Task Text', () => {
expect(result.assignees[1]).toBe('user2')
})
it('should parse an assignee with a space in it', () => {
const text = `Lorem Ipsum @'user with long name'`
const text = 'Lorem Ipsum @\'user with long name\''
const result = parseTaskText(text)
expect(result.text).toBe(text)
@@ -732,7 +732,7 @@ describe('Parse Task Text', () => {
expect(result.assignees[0]).toBe('user with long name')
})
it('should parse an assignee with a space in it and "', () => {
const text = `Lorem Ipsum @"user with long name"`
const text = 'Lorem Ipsum @"user with long name"'
const result = parseTaskText(text)
expect(result.text).toBe(text)
@@ -740,7 +740,7 @@ describe('Parse Task Text', () => {
expect(result.assignees[0]).toBe('user with long name')
})
it('should parse an assignee who is called like a date as assignee', () => {
const text = `Lorem Ipsum @today`
const text = 'Lorem Ipsum @today'
const result = parseTaskText(text)
expect(result.text).toBe(text)
-2
View File
@@ -1,5 +1,3 @@
/* eslint-disable no-console */
import {register} from 'register-service-worker'
import {getFullBaseUrl} from './helpers/getFullBaseUrl'
+1 -1
View File
@@ -247,7 +247,7 @@ export const useAuthStore = defineStore('auth', () => {
isAuthenticated = info.exp >= ts
// Settings should only be loaded from the api request, not via the jwt
setUser(info, false)
} catch (e) {
} catch (_) {
logout()
}
+18 -3
View File
@@ -1,3 +1,4 @@
@use "sass:math";
//
// IMPORTANT NOTE:
//
@@ -10,10 +11,17 @@
// are defined here.
//
$family-sans-serif: 'Open Sans', Helvetica, Arial, sans-serif;
// the default values get overwritten by the definitions above
@import "bulma-css-variables/sass/utilities/_all";
// utilities
@forward "bulma/sass/utilities/initial-variables.scss" with (
$family-sans-serif: "'Open Sans', Helvetica, Arial, sans-serif"
);
@forward "bulma/sass/utilities/functions";
@forward "bulma/sass/utilities/derived-variables";
@forward "bulma/sass/utilities/controls";
@use "bulma/sass/utilities/initial-variables.scss" as *;
// since $tablet is defined by bulma we can just define it after importing the utilities
$mobile: math.div($tablet, 2);
@@ -39,3 +47,10 @@ $vikunja-nav-color: var(--grey-700);
$vikunja-nav-selected-width: 0.4rem;
$close-button-min-space: 84px;
// re-added from bulma-css-variables
$item-padding: 0.375rem 1rem;
// 1152px container + 4rem
$widescreen: 1152px + (2 * $gap);
$border-light: $grey-lightest;
+4 -4
View File
@@ -1,4 +1,4 @@
@import "tooltip";
@import "labels";
@import "task";
@import "tasks";
@forward "tooltip";
@forward "labels";
@forward "task";
@forward "tasks";
@@ -1,3 +1,5 @@
@use "@/styles/common-imports" as *;
// FIXME: These classes are used all over.
// very hard to untangle
// they have many overwrites at different positions
@@ -1,2 +1,2 @@
@import "colors";
@import "shadows";
@forward "colors";
@forward "shadows";
+76 -70
View File
@@ -1,11 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@import "fonts";
@import "transitions";
// ###################
// START BULMA IMPORTS
// ###################
@@ -14,79 +6,93 @@
// with the expeption of the bulma utilities – They are
// imported globally in common-imports.scss
// imports from "bulma-css-variables/sass/base/_all";
@import "bulma-css-variables/sass/base/minireset";
@import "bulma-css-variables/sass/base/generic";
@import "bulma-css-variables/sass/base/animations";
// themes
@forward "bulma/sass/themes";
// base
@forward "bulma/sass/base/minireset";
@forward "bulma/sass/base/generic";
@forward "bulma/sass/base/animations";
// imports from "bulma-css-variables/sass/elements/_all";
@import "bulma-css-variables/sass/elements/box";
@import "bulma-css-variables/sass/elements/button";
@import "bulma-css-variables/sass/elements/container";
@import "bulma-css-variables/sass/elements/content";
@import "bulma-css-variables/sass/elements/icon";
@import "bulma-css-variables/sass/elements/image";
//@import "bulma-css-variables/sass/elements/notification"; // not used
// @import "bulma-css-variables/sass/elements/progress"; // not used
@import "bulma-css-variables/sass/elements/table";
@import "bulma-css-variables/sass/elements/tag";
@import "bulma-css-variables/sass/elements/title";
// elements
@forward "bulma/sass/elements/block";
@forward "bulma/sass/elements/box";
@forward "bulma/sass/elements/button";
@forward "bulma/sass/elements/content";
@forward "bulma/sass/elements/delete";
@forward "bulma/sass/elements/icon";
@forward "bulma/sass/elements/image";
@forward "bulma/sass/elements/loader";
// @forward "bulma/sass/elements/notification"; // not used
// @forward "bulma/sass/elements/progress"; // not used
@forward "bulma/sass/elements/table";
@forward "bulma/sass/elements/tag";
@forward "bulma/sass/elements/title";
@import "bulma-css-variables/sass/elements/other";
// form
@forward "bulma/sass/form/shared";
@forward "bulma/sass/form/input-textarea";
@forward "bulma/sass/form/checkbox-radio";
@forward "bulma/sass/form/select";
@forward "bulma/sass/form/file";
@forward "bulma/sass/form/tools";
// components
// @forward "bulma/sass/components/breadcrumb"; // not used
@forward "bulma/sass/components/card";
// @forward "bulma/sass/components/dropdown"; // not used
@forward "bulma/sass/components/menu";
// @forward "bulma/sass/components/message"; // not used
@forward "bulma/sass/components/modal";
@forward "bulma/sass/components/navbar";
@forward "bulma/sass/components/pagination";
// @forward "bulma/sass/components/panel"; // not used
// @forward "bulma/sass/components/tabs"; // not used
// imports from "bulma-css-variables/sass/form/_all";
@import "bulma-css-variables/sass/form/shared";
@import "bulma-css-variables/sass/form/input-textarea";
@import "bulma-css-variables/sass/form/checkbox-radio";
@import "bulma-css-variables/sass/form/select";
@import "bulma-css-variables/sass/form/file";
@import "bulma-css-variables/sass/form/tools";
// grid
@forward "bulma/sass/grid/columns";
// @forward "bulma/sass/grid/grid"; // probably not used, because new
// layout
@forward "bulma/sass/layout/container";
// @forward "bulma/sass/layout/footer"; // not used
// @forward "bulma/sass/layout/hero"; // not used
// @forward "bulma/sass/layout/level"; // not used
@forward "bulma/sass/layout/media";
// @forward "bulma/sass/layout/section"; // not used
// imports from "bulma-css-variables/sass/components/_all";
// @import "bulma-css-variables/sass/components/breadcrumb"; // not used
@import "bulma-css-variables/sass/components/card";
// @import "bulma-css-variables/sass/components/dropdown"; // moved to component
// @import "bulma-css-variables/sass/components/level"; // not used
@import "bulma-css-variables/sass/components/media";
@import "bulma-css-variables/sass/components/menu";
//@import "bulma-css-variables/sass/components/message"; // not used
@import "bulma-css-variables/sass/components/modal";
@import "bulma-css-variables/sass/components/navbar";
@import "bulma-css-variables/sass/components/pagination";
// @import "bulma-css-variables/sass/components/panel"; // not used
// @import "bulma-css-variables/sass/components/tabs"; // not used
// base/skeleton
@forward "bulma/sass/base/skeleton";
// imports from "bulma-css-variables/sass/grid/_all";
@import "bulma-css-variables/sass/grid/columns";
// @import "bulma-css-variables/sass/grid/tiles"; // not used
// imports from "bulma-css-variables/sass/helpers/_all";
@import "bulma-css-variables/sass/helpers/color";
@import "bulma-css-variables/sass/helpers/flexbox";
@import "bulma-css-variables/sass/helpers/float";
// @import "bulma-css-variables/sass/helpers/other"; // not used
// @import "bulma-css-variables/sass/helpers/overflow";
// @import "bulma-css-variables/sass/helpers/position";
@import "bulma-css-variables/sass/helpers/spacing";
@import "bulma-css-variables/sass/helpers/typography";
@import "bulma-css-variables/sass/helpers/visibility";
// imports from "bulma-css-variables/sass/layout/_all";
// @import "bulma-css-variables/sass/layout/hero"; // not used
// @import "bulma-css-variables/sass/layout/section"; // not used
// @import "bulma-css-variables/sass/layout/footer"; // not used
// helpers
// @forward "bulma/sass/helpers/aspect-ratio"; // probably not used, because new
// @forward "bulma/sass/helpers/border"; // probably not used, because new
@forward "bulma/sass/helpers/color";
@forward "bulma/sass/helpers/flexbox";
@forward "bulma/sass/helpers/float";
// @forward "bulma/sass/helpers/gap"; // probably not used, because new
// @forward "bulma/sass/helpers/overflow"; // not used
// @forward "bulma/sass/helpers/position"; // not used
@forward "bulma/sass/helpers/spacing";
@forward "bulma/sass/helpers/typography";
@forward "bulma/sass/helpers/visibility";
// @forward "bulma/sass/helpers/other"; // not used
// #################
// END BULMA IMPORTS
// #################
@import "theme";
@import "components";
@import "custom-properties";
@use "fonts";
@use "transitions";
@use "theme";
@use "components";
@use "custom-properties";
@tailwind base;
@tailwind components;
@tailwind utilities;
+10 -10
View File
@@ -1,12 +1,12 @@
@import "scrollbars";
@forward "scrollbars";
@import "theme";
@forward "theme";
@import "background";
@import "content";
@import "form";
@import "link-share";
@import "loading";
@import "flatpickr";
@import 'helpers';
@import 'navigation';
@forward "background";
@forward "content";
@forward "form";
@forward "link-share";
@forward "loading";
@forward "flatpickr";
@forward 'helpers';
@forward 'navigation';
@@ -1,3 +1,5 @@
@use "@/styles/common-imports" as *;
.app-container.has-background,
.link-share-container.has-background {
position: relative;
+2
View File
@@ -1,3 +1,5 @@
@use "@/styles/common-imports" as *;
.content h3 {
.icon,
.is-small {
+7 -4
View File
@@ -1,3 +1,6 @@
@use 'sass:list';
@use "@/styles/common-imports" as *;
.field.has-addons .button {
height: 2.5rem;
margin-left: 0 !important;
@@ -20,8 +23,8 @@
}
@each $name, $pair in $colors {
$color: nth($pair, 1);
$color-invert: nth($pair, 2);
$color: list.nth($pair, 1);
$color-invert: list.nth($pair, 2);
&.is-#{$name} {
&.is-active,
@@ -85,8 +88,8 @@
padding: .3rem;
&:focus {
background: $input-background-color;
border-color: $input-focus-border-color;
background: $scheme-main;
border-color: $link;
}
&.disabled {
@@ -1,3 +1,5 @@
@use "@/styles/common-imports" as *;
.field.has-addons.no-input-mobile {
.control:first-child {
width: 100%;
+2
View File
@@ -1,3 +1,5 @@
@use "bulma/sass/utilities/mixins.scss" as *;
// FIXME: move to Loading.vue
.loader-container.is-loading {
position: relative;
@@ -1,3 +1,5 @@
@use "@/styles/common-imports" as *;
// these are general menu styles
// should be in own components
.menu {
@@ -1,3 +1,5 @@
@use "@/styles/common-imports" as *;
$scrollbar-height: 8px;
$scrollbar-track-color: var(--grey-200);
$scrollbar-thumb-color: var(--grey-300);
+2
View File
@@ -1,3 +1,5 @@
@use "@/styles/common-imports" as *;
*,
*:hover,
*:active,
+2
View File
@@ -1,3 +1,5 @@
@use "@/styles/common-imports" as *;
.fade-enter-active,
.fade-leave-active {
transition: opacity $transition-duration;
+1 -4
View File
@@ -1,12 +1,9 @@
/* eslint-disable no-console */
/* eslint-disable no-undef */
import {getFullBaseUrl} from './helpers/getFullBaseUrl'
declare let self: ServiceWorkerGlobalScope
const fullBaseUrl = getFullBaseUrl()
const workboxVersion = 'v7.1.0'
const workboxVersion = 'v7.3.0'
importScripts(`${fullBaseUrl}workbox-${workboxVersion}/workbox-sw.js`)
workbox.setConfig({
@@ -88,7 +88,7 @@ export function useGanttTaskList<F extends Filters>(
// update the task with possible changes from server
tasks.value.set(updatedTask.id, updatedTask)
success('Saved')
} catch (e) {
} catch (_) {
error('Something went wrong saving the task')
// roll back changes
tasks.value.set(task.id, oldTask)
-1
View File
@@ -117,7 +117,6 @@ const showAll = computed(() => typeof props.dateFrom === 'undefined' || typeof p
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(([, value]) => props.dateFrom === value[0] && props.dateTo === value[1])
?.[0]
if (typeof predefinedRange !== 'undefined') {

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