Compare commits

..
Author SHA1 Message Date
kolaente 08821ea8a8 chore: 0.24.2 release preperations 2024-08-12 20:22:07 +02:00
kolaente e9a466fa31 fix(kanban): always make cover image full width
(cherry picked from commit f22420d502)
2024-08-12 20:17:10 +02:00
kolaente de9f686480 fix(migration): ensure project background gets exported and imported
(cherry picked from commit ebfd5f54d2)
2024-08-12 20:17:10 +02:00
kolaente d08e9650ba fix(migration): make sure tasks are associated to the correct view and bucket for data imported from Vikunja dump
This change fixes a bug where imported projects would contain the default views additionally to the ones included in the export. This also caused the tasks to not show up in the views and buckets where they should show up, the newly imported ones.

Resolves https://community.vikunja.io/t/migration-from-vikunja-export-duplicated-boards-local-to-oidc/2690

(cherry picked from commit 28b4eaee31)
2024-08-12 20:17:09 +02:00
kolaente 0434a96c3a fix(views): do not create task bucket and task position entries when duplicating a project
Resolves https://github.com/go-vikunja/vikunja/issues/297
Resolves https://community.vikunja.io/t/duplicating-a-board-puts-the-buckets-in-the-original-board/2579

(cherry picked from commit b83448b7a6)
2024-08-12 20:17:07 +02:00
kolaente 2b9b77bef2 fix(task): move task into new kanban bucket when moving between projects
Resolves https://github.com/go-vikunja/vikunja/issues/290
Resolves https://community.vikunja.io/t/tasks-moved-from-one-project-to-another-do-not-show-up-in-kanban/2689

(cherry picked from commit eed783e42f)
2024-08-12 20:17:05 +02:00
kolaente f5040ad2f4 chore: use nixpkgs unstable for more recent packages
(cherry picked from commit 1d624612ee)
2024-08-12 20:17:04 +02:00
kolaente 7bc77ae8c0 fix(task): set current project after moving a task
(cherry picked from commit 9576f72d7c)
2024-08-12 20:17:02 +02:00
kolaente 715269a5d0 fix(task): do not allow moving a task to the project the task already belongs to
(cherry picked from commit 7efc4d1bc8)
2024-08-12 20:17:01 +02:00
kolaente 53605c24f0 chore(deps): update dependency node to v20.16.0
(cherry picked from commit 670e605275)
2024-08-12 20:17:00 +02:00
kolaente da0b741b69 chore(deps): update go toolchain to 1.22.5
(cherry picked from commit 871b3ce071)
2024-08-12 20:16:59 +02:00
kolaente 73f923bc47 feat: switch from nix flakes to devenv
(cherry picked from commit ad3c5fcee5)
2024-08-12 20:16:56 +02:00
kolaente 25a8c7ea80 feat(editor): support custom protocol for links
Resolves https://github.com/go-vikunja/vikunja/issues/306

(cherry picked from commit e2a87036e0)
2024-08-12 20:16:55 +02:00
kolaente 950de7c954 fix(mail): do not fail testmail command when the connection could not be closed.
Resolves https://github.com/go-vikunja/vikunja/issues/300

(cherry picked from commit 40bb86bee5)
2024-08-12 20:16:54 +02:00
Frederick [Bot]andkolaente 42ba9240b0 chore(i18n): update translations via Crowdin
(cherry picked from commit 4a4ed2cf89)
2024-08-12 20:16:52 +02:00
kolaente 3da6299021 chore(deps): update flake
(cherry picked from commit 7751e124a7)
2024-08-12 20:16:50 +02:00
Frederick [Bot]andkolaente 1ac0f412b9 chore(i18n): update translations via Crowdin
(cherry picked from commit 6137d07138)
2024-08-12 20:16:48 +02:00
kolaente e7fbdc2727 fix(i18n): change casing of Ukrainian language in selector
(cherry picked from commit 66d316f0b5)
2024-08-12 20:16:46 +02:00
kolaente 7117303d57 docs: clarify Todoist redirect url
Resolves https://github.com/go-vikunja/vikunja/issues/302

(cherry picked from commit bf2d56c9d4)
2024-08-12 20:16:45 +02:00
Dominik Pschenitschniandkolaente 0ca43dc147 fix: remove console log
(cherry picked from commit 413798b321)
2024-08-12 20:16:43 +02:00
Frederick [Bot]andkolaente c47ea2716c chore(i18n): update translations via Crowdin
(cherry picked from commit bf1c8c6119)
2024-08-12 20:16:42 +02:00
Frederick [Bot]andkolaente cbdcc61414 chore(i18n): update translations via Crowdin
(cherry picked from commit 49fa0a8468)
2024-08-12 20:16:41 +02:00
Frederick [Bot]andkolaente 194a7d4cf8 chore(i18n): update translations via Crowdin
(cherry picked from commit 784cd42a7a)
2024-08-12 20:16:37 +02:00
Frederick [Bot]andkolaente 9695785a0c chore(i18n): update translations via Crowdin
(cherry picked from commit 4f74be0247)
2024-08-12 20:16:35 +02:00
Dominik Pschenitschniandkolaente d9f555554e fix: textarea autosize for LanguageTool
(cherry picked from commit 7ef1e0a3e5)
2024-08-12 20:16:32 +02:00
Dominik Pschenitschniandkolaente 6e38bcf349 chore: improve error message
(cherry picked from commit bc5fd380e5)
2024-08-12 20:16:30 +02:00
Dominik Pschenitschniandkolaente fc780a90ae chore: remove lodash.debounce
(cherry picked from commit bcd306b84d)
2024-08-12 20:16:28 +02:00
Dominik Pschenitschniandkolaente d586f691b7 feat: add missing peer dependency
(cherry picked from commit ad2870335d)
2024-08-12 20:16:26 +02:00
Frederick [Bot]andkolaente 9e8e43bd12 chore(i18n): update translations via Crowdin
(cherry picked from commit 917748777f)
2024-08-12 20:16:24 +02:00
renovateandkolaente c554a96b4b fix(deps): update module github.com/coreos/go-oidc/v3 to v3.11.0
(cherry picked from commit 324c966dda)
2024-08-12 20:16:23 +02:00
Frederick [Bot]andkolaente 3ecc81094f [skip ci] Updated swagger docs
(cherry picked from commit 0984821b41)
2024-08-12 20:16:21 +02:00
Dominik Pschenitschniandkolaente 811ccc1baa feat: improve ProjectSettingsViews
(cherry picked from commit e8be657d97)
2024-08-12 20:16:17 +02:00
Dominik Pschenitschniandkolaente d7c5451729 feat: add tailwind with prefix (#2513)
Reviewed-on: https://kolaente.dev/vikunja/vikunja/pulls/2513
Co-authored-by: Dominik Pschenitschni <mail@celement.de>
Co-committed-by: Dominik Pschenitschni <mail@celement.de>
(cherry picked from commit bbfd5270db)
2024-08-12 20:16:16 +02:00
Dominik Pschenitschniandkolaente 96884372b4 fix(projects): trigger only single mutation
Previously these store methods created multiple edits on deep objects in the store or edited a deep nested object directly.

This is bad because:
- multiple edits lead to multiple triggers of all the watchers on the project.
- in theory we should listen deep to a project if we use some deep reactive value of a project, if we want deep updates. Because this is easy to forget, it's better to update the project directly. For this the method `setProject` already existed in the store. This is no real overhead because Vue is smart enough to only trigger listeners that use data of the modified state.

By modifying only a copy of the view and submitting the modified result __once__ we can save us a lot of headache.

PS: I'm not sure if there were any visible problems, because Vue is really fast and the reactivity system works quite well. Regardless of this we should try not to modify state unnecessarily.
(cherry picked from commit 1e632397d2)
2024-08-12 20:16:15 +02:00
Dominik Pschenitschniandkolaente dddba4d64a feat: improve priority visibility
(cherry picked from commit d35454c099)
2024-08-12 20:16:13 +02:00
Dominik Pschenitschniandkolaente a0e3efe2d1 feat: improve label store
(cherry picked from commit 2a6ba7e7f0)
2024-08-12 20:16:12 +02:00
Dominik Pschenitschniandkolaente d707e1576a feat: improve projects store
(cherry picked from commit 1e523a1a39)
2024-08-12 20:16:11 +02:00
Dominik Pschenitschniandkolaente 3aaf363413 fix: emit for DatepickerWithValues
(cherry picked from commit fb91e73a3c)
2024-08-12 20:16:08 +02:00
Frederick [Bot]andkolaente 28fff10fec chore(i18n): update translations via Crowdin
(cherry picked from commit ebd6139c1f)
2024-08-12 20:16:07 +02:00
Dominik Pschenitschniandkolaente 8729c24e1d feat: use withDefaults for Reminders
(cherry picked from commit 6990be705c)
2024-08-12 20:16:01 +02:00
233 changed files with 4038 additions and 6498 deletions
+4 -4
View File
@@ -139,7 +139,7 @@ steps:
event: [ push, tag, pull_request ]
- name: api-lint
image: golangci/golangci-lint:v1.61.0
image: golangci/golangci-lint:v1.59.1
pull: always
environment:
GOPROXY: 'https://goproxy.kolaente.de'
@@ -716,7 +716,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.38.0
pull: always
commands:
- apk add git go
@@ -732,7 +732,7 @@ steps:
depends_on: [ after-build-compress ]
- name: build-os-packages-version
image: goreleaser/nfpm:v2.40.0
image: goreleaser/nfpm:v2.38.0
pull: always
commands:
- apk add git go
@@ -1352,6 +1352,6 @@ steps:
- failure
---
kind: signature
hmac: eff6e40dc763fa1395078daaabcbef2aaa74fe0a36818a87e89015dbc4e9dae4
hmac: dc96a5f8aaa332b35fc02f1b8566493e494452d62bed52973d804f5347ae47ae
...
-1
View File
@@ -28,7 +28,6 @@ vendor/
os-packages/
mage_output_file.go
mage-static
.DS_Store
# Devenv
.devenv*
-10
View File
@@ -109,13 +109,3 @@ issues:
text: 'structtag: struct field Position repeats json tag "position" also at'
linters:
- govet
- path: pkg/cmd/user.go
text: 'G115: integer overflow conversion uintptr -> int'
linters:
- gosec
- text: 'G115: integer overflow conversion int64 -> uint64'
linters:
- gosec
- text: 'G115: integer overflow conversion int -> uint64'
linters:
- gosec
+11 -13
View File
@@ -1,15 +1,13 @@
{
"recommendations": [
"codezombiech.gitignore",
"dbaeumer.vscode-eslint",
"editorconfig.editorconfig",
"vue.volar",
"lokalise.i18n-ally",
"mgmcdermott.vscode-language-babel",
"mikestead.dotenv",
"Syler.sass-indented",
"vitest.explorer",
"mkhl.direnv",
"golang.Go"
]
"recommendations": [
"codezombiech.gitignore",
"dbaeumer.vscode-eslint",
"editorconfig.editorconfig",
"vue.volar",
"lokalise.i18n-ally",
"mgmcdermott.vscode-language-babel",
"mikestead.dotenv",
"Syler.sass-indented",
"vitest.explorer"
]
}
+5 -12
View File
@@ -3,9 +3,9 @@ FROM --platform=$BUILDPLATFORM node:20.16.0-alpine AS frontendbuilder
WORKDIR /build
ENV PNPM_CACHE_FOLDER=.cache/pnpm/
ENV PUPPETEER_SKIP_DOWNLOAD=true
ENV CYPRESS_INSTALL_BINARY=0
ENV PNPM_CACHE_FOLDER .cache/pnpm/
ENV PUPPETEER_SKIP_DOWNLOAD true
ENV CYPRESS_INSTALL_BINARY 0
COPY frontend/ ./
@@ -24,7 +24,7 @@ COPY --from=frontendbuilder /build/dist ./frontend/dist
ARG TARGETOS TARGETARCH TARGETVARIANT
ENV GOPROXY=https://goproxy.kolaente.de
ENV GOPROXY https://goproxy.kolaente.de
RUN export PATH=$PATH:$GOPATH/bin && \
mage build:clean && \
mage release:xgo "${TARGETOS}/${TARGETARCH}/${TARGETVARIANT}"
@@ -35,14 +35,7 @@ RUN export PATH=$PATH:$GOPATH/bin && \
# The actual image
FROM scratch
LABEL org.opencontainers.image.authors='maintainers@vikunja.io'
LABEL org.opencontainers.image.url='https://vikunja.io'
LABEL org.opencontainers.image.documentation='https://vikunja.io/docs'
LABEL org.opencontainers.image.source='https://code.vikunja.io/vikunja'
LABEL org.opencontainers.image.licenses='AGPLv3'
LABEL org.opencontainers.image.title='Vikunja'
LABEL maintainer="maintainers@vikunja.io"
WORKDIR /app/vikunja
ENTRYPOINT [ "/app/vikunja/vikunja" ]
EXPOSE 3456
+2 -2
View File
@@ -51,11 +51,11 @@
}
},
"devDependencies": {
"electron": "29.4.6",
"electron": "29.4.5",
"electron-builder": "24.13.3"
},
"dependencies": {
"connect-history-api-fallback": "2.0.0",
"express": "4.21.0"
"express": "4.19.2"
}
}
+34 -39
View File
@@ -9,13 +9,13 @@ dependencies:
specifier: 2.0.0
version: 2.0.0
express:
specifier: 4.21.0
version: 4.21.0
specifier: 4.19.2
version: 4.19.2
devDependencies:
electron:
specifier: 29.4.6
version: 29.4.6
specifier: 29.4.5
version: 29.4.5
electron-builder:
specifier: 24.13.3
version: 24.13.3(electron-builder-squirrel-windows@24.13.3)
@@ -445,8 +445,8 @@ packages:
resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==}
dev: true
/body-parser@1.20.3:
resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==}
/body-parser@1.20.2:
resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==}
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
dependencies:
bytes: 3.1.2
@@ -457,7 +457,7 @@ packages:
http-errors: 2.0.0
iconv-lite: 0.4.24
on-finished: 2.4.1
qs: 6.13.0
qs: 6.11.0
raw-body: 2.5.2
type-is: 1.6.18
unpipe: 1.0.0
@@ -917,8 +917,8 @@ packages:
- supports-color
dev: true
/electron@29.4.6:
resolution: {integrity: sha512-fz8ndj8cmmf441t4Yh2FDP3Rn0JhLkVGvtUf2YVMbJ5SdJPlc0JWll9jYkhh60jDKVVCr/tBAmfxqRnXMWJpzg==}
/electron@29.4.5:
resolution: {integrity: sha512-DlEuzGbWBYl1Qr0qUYgNZdoixJg4YGHy2HC6fkRjSXSlb01UrQ5ORi8hNLzelzyYx8rNQyyE3zDUuk9EnZwYuA==}
engines: {node: '>= 12.20.55'}
hasBin: true
requiresBuild: true
@@ -943,11 +943,6 @@ packages:
engines: {node: '>= 0.8'}
dev: false
/encodeurl@2.0.0:
resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==}
engines: {node: '>= 0.8'}
dev: false
/end-of-stream@1.4.4:
resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==}
dependencies:
@@ -1000,36 +995,36 @@ packages:
engines: {node: '>= 0.6'}
dev: false
/express@4.21.0:
resolution: {integrity: sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==}
/express@4.19.2:
resolution: {integrity: sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==}
engines: {node: '>= 0.10.0'}
dependencies:
accepts: 1.3.8
array-flatten: 1.1.1
body-parser: 1.20.3
body-parser: 1.20.2
content-disposition: 0.5.4
content-type: 1.0.5
cookie: 0.6.0
cookie-signature: 1.0.6
debug: 2.6.9
depd: 2.0.0
encodeurl: 2.0.0
encodeurl: 1.0.2
escape-html: 1.0.3
etag: 1.8.1
finalhandler: 1.3.1
finalhandler: 1.2.0
fresh: 0.5.2
http-errors: 2.0.0
merge-descriptors: 1.0.3
merge-descriptors: 1.0.1
methods: 1.1.2
on-finished: 2.4.1
parseurl: 1.3.3
path-to-regexp: 0.1.10
path-to-regexp: 0.1.7
proxy-addr: 2.0.7
qs: 6.13.0
qs: 6.11.0
range-parser: 1.2.1
safe-buffer: 5.2.1
send: 0.19.0
serve-static: 1.16.2
send: 0.18.0
serve-static: 1.15.0
setprototypeof: 1.2.0
statuses: 2.0.1
type-is: 1.6.18
@@ -1082,12 +1077,12 @@ packages:
minimatch: 5.1.6
dev: true
/finalhandler@1.3.1:
resolution: {integrity: sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==}
/finalhandler@1.2.0:
resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==}
engines: {node: '>= 0.8'}
dependencies:
debug: 2.6.9
encodeurl: 2.0.0
encodeurl: 1.0.2
escape-html: 1.0.3
on-finished: 2.4.1
parseurl: 1.3.3
@@ -1548,8 +1543,8 @@ packages:
engines: {node: '>= 0.6'}
dev: false
/merge-descriptors@1.0.3:
resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==}
/merge-descriptors@1.0.1:
resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==}
dev: false
/methods@1.1.2:
@@ -1729,8 +1724,8 @@ packages:
minipass: 7.0.4
dev: true
/path-to-regexp@0.1.10:
resolution: {integrity: sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==}
/path-to-regexp@0.1.7:
resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==}
dev: false
/pend@1.2.0:
@@ -1785,8 +1780,8 @@ packages:
requiresBuild: true
dev: true
/qs@6.13.0:
resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==}
/qs@6.11.0:
resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==}
engines: {node: '>=0.6'}
dependencies:
side-channel: 1.0.6
@@ -1924,8 +1919,8 @@ packages:
lru-cache: 6.0.0
dev: true
/send@0.19.0:
resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==}
/send@0.18.0:
resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==}
engines: {node: '>= 0.8.0'}
dependencies:
debug: 2.6.9
@@ -1954,14 +1949,14 @@ packages:
dev: true
optional: true
/serve-static@1.16.2:
resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==}
/serve-static@1.15.0:
resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==}
engines: {node: '>= 0.8.0'}
dependencies:
encodeurl: 2.0.0
encodeurl: 1.0.2
escape-html: 1.0.3
parseurl: 1.3.3
send: 0.19.0
send: 0.18.0
transitivePeerDependencies:
- supports-color
dev: false
+12 -12
View File
@@ -3,11 +3,11 @@
"devenv": {
"locked": {
"dir": "src/modules",
"lastModified": 1726063457,
"lastModified": 1723409744,
"owner": "cachix",
"repo": "devenv",
"rev": "39bf6ce569103c9390d37322daa59468c31b3ce7",
"treeHash": "839747a1cb35ba6d5b36cce9a739ab2ba5e4a5d4",
"rev": "116159a4bfeaa79662f4c39dbfaf2fce6a5737f7",
"treeHash": "ccbae63055f83e669996a88a3cbb4ef69febd002",
"type": "github"
},
"original": {
@@ -72,11 +72,11 @@
},
"nixpkgs-stable": {
"locked": {
"lastModified": 1725930920,
"lastModified": 1723282977,
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "44a71ff39c182edaf25a7ace5c9454e7cba2c658",
"treeHash": "56e93544112b7bb7aa0c3093d537295683ef9148",
"rev": "a781ff33ae258bbcfd4ed6e673860c3e923bf2cc",
"treeHash": "75cf71293251c603e56d6076098346b4128b2ec8",
"type": "github"
},
"original": {
@@ -88,11 +88,11 @@
},
"nixpkgs-unstable": {
"locked": {
"lastModified": 1725983898,
"lastModified": 1723175592,
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "1355a0cbfeac61d785b7183c0caaec1f97361b43",
"treeHash": "d8ce0c1af7690da6161f0991484661331f5bc999",
"rev": "5e0ca22929f3342b19569b21b2f3462f053e497b",
"treeHash": "a6b38d5232ae0ae0c666c37f5ec9620c14f30369",
"type": "github"
},
"original": {
@@ -112,11 +112,11 @@
"nixpkgs-stable": "nixpkgs-stable"
},
"locked": {
"lastModified": 1725513492,
"lastModified": 1723202784,
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"rev": "7570de7b9b504cfe92025dd1be797bf546f66528",
"treeHash": "4b46d77870afecd8f642541cb4f4927326343b59",
"rev": "c7012d0c18567c889b948781bc74a501e92275d1",
"treeHash": "0016d5d748d61d3bfaa69d3c02abff74740a46b7",
"type": "github"
},
"original": {
+3 -9
View File
@@ -5,14 +5,13 @@ let
in {
packages = with pkgs-unstable; [
# General tools
git-cliff
git-cliff
# Frontend tools
cypress
# API tools
golangci-lint mage
# Desktop
electron
] ++ lib.optionals (!pkgs.stdenv.isDarwin) [
# Frontend tools (exclude on Darwin)
cypress
];
languages = {
@@ -29,9 +28,4 @@ in {
enable = true;
};
};
services.mailpit = {
enable = true;
package = pkgs-unstable.mailpit;
};
}
-1
View File
@@ -13,7 +13,6 @@ node_modules
dist
coverage
*.zip
.vite/
# Test files
cypress/screenshots
+2 -2
View File
@@ -484,7 +484,7 @@ describe('Task', () => {
addLabelToTaskAndVerify(labels[0].title)
cy.get('.modal-container > .close')
cy.get('.modal-content .close')
.click()
cy.get('.bucket .task')
@@ -850,7 +850,7 @@ describe('Task', () => {
uploadAttachmentAndVerify(tasks[0].id)
cy.get('.modal-container > .close')
cy.get('.modal-content .close')
.click()
cy.get('.bucket .task .footer .icon svg.fa-paperclip')
+75 -75
View File
@@ -13,7 +13,7 @@
},
"homepage": "https://vikunja.io/",
"funding": "https://opencollective.com/vikunja",
"packageManager": "pnpm@9.10.0",
"packageManager": "pnpm@9.6.0",
"keywords": [
"todo",
"productivity",
@@ -56,131 +56,131 @@
"@fortawesome/vue-fontawesome": "3.0.8",
"@github/hotkey": "3.1.1",
"@infectoone/vue-ganttastic": "2.3.2",
"@intlify/unplugin-vue-i18n": "5.0.0",
"@kyvg/vue3-notification": "3.3.0",
"@intlify/unplugin-vue-i18n": "4.0.0",
"@kyvg/vue3-notification": "3.2.1",
"@sentry/tracing": "7.114.0",
"@sentry/vue": "8.30.0",
"@tiptap/core": "2.7.1",
"@tiptap/extension-blockquote": "2.7.1",
"@tiptap/extension-bold": "2.7.1",
"@tiptap/extension-bullet-list": "2.7.1",
"@tiptap/extension-code": "2.7.1",
"@tiptap/extension-code-block": "2.7.1",
"@tiptap/extension-code-block-lowlight": "2.7.1",
"@tiptap/extension-document": "2.7.1",
"@tiptap/extension-dropcursor": "2.7.1",
"@tiptap/extension-gapcursor": "2.7.1",
"@tiptap/extension-hard-break": "2.7.1",
"@tiptap/extension-heading": "2.7.1",
"@tiptap/extension-history": "2.7.1",
"@tiptap/extension-horizontal-rule": "2.7.1",
"@tiptap/extension-image": "2.7.1",
"@tiptap/extension-italic": "2.7.1",
"@tiptap/extension-link": "2.7.1",
"@tiptap/extension-list-item": "2.7.1",
"@tiptap/extension-ordered-list": "2.7.1",
"@tiptap/extension-paragraph": "2.7.1",
"@tiptap/extension-placeholder": "2.7.1",
"@tiptap/extension-strike": "2.7.1",
"@tiptap/extension-table": "2.7.1",
"@tiptap/extension-table-cell": "2.7.1",
"@tiptap/extension-table-header": "2.7.1",
"@tiptap/extension-table-row": "2.7.1",
"@tiptap/extension-task-item": "2.7.1",
"@tiptap/extension-task-list": "2.7.1",
"@tiptap/extension-text": "2.7.1",
"@tiptap/extension-typography": "2.7.1",
"@tiptap/extension-underline": "2.7.1",
"@tiptap/pm": "2.7.1",
"@tiptap/suggestion": "2.7.1",
"@tiptap/vue-3": "2.7.1",
"@vueuse/core": "11.1.0",
"@vueuse/router": "11.1.0",
"axios": "1.7.7",
"@sentry/vue": "8.18.0",
"@tiptap/core": "2.5.4",
"@tiptap/extension-blockquote": "2.5.4",
"@tiptap/extension-bold": "2.5.4",
"@tiptap/extension-bullet-list": "2.5.4",
"@tiptap/extension-code": "2.5.4",
"@tiptap/extension-code-block": "2.5.4",
"@tiptap/extension-code-block-lowlight": "2.5.4",
"@tiptap/extension-document": "2.5.4",
"@tiptap/extension-dropcursor": "2.5.4",
"@tiptap/extension-gapcursor": "2.5.4",
"@tiptap/extension-hard-break": "2.5.4",
"@tiptap/extension-heading": "2.5.4",
"@tiptap/extension-history": "2.5.4",
"@tiptap/extension-horizontal-rule": "2.5.4",
"@tiptap/extension-image": "2.5.4",
"@tiptap/extension-italic": "2.5.4",
"@tiptap/extension-link": "2.5.4",
"@tiptap/extension-list-item": "2.5.4",
"@tiptap/extension-ordered-list": "2.5.4",
"@tiptap/extension-paragraph": "2.5.4",
"@tiptap/extension-placeholder": "2.5.4",
"@tiptap/extension-strike": "2.5.4",
"@tiptap/extension-table": "2.5.4",
"@tiptap/extension-table-cell": "2.5.4",
"@tiptap/extension-table-header": "2.5.4",
"@tiptap/extension-table-row": "2.5.4",
"@tiptap/extension-task-item": "2.5.4",
"@tiptap/extension-task-list": "2.5.4",
"@tiptap/extension-text": "2.5.4",
"@tiptap/extension-typography": "2.5.4",
"@tiptap/extension-underline": "2.5.4",
"@tiptap/pm": "2.5.4",
"@tiptap/suggestion": "2.5.4",
"@tiptap/vue-3": "2.5.4",
"@vueuse/core": "10.11.0",
"@vueuse/router": "10.11.0",
"axios": "1.7.2",
"blurhash": "2.0.5",
"bulma-css-variables": "0.9.33",
"change-case": "5.4.4",
"date-fns": "4.1.0",
"dayjs": "1.11.13",
"date-fns": "3.6.0",
"dayjs": "1.11.12",
"dompurify": "3.1.6",
"fast-deep-equal": "3.1.3",
"flatpickr": "4.6.13",
"flexsearch": "0.7.43",
"flexsearch": "0.7.31",
"floating-vue": "5.2.2",
"is-touch-device": "1.0.1",
"klona": "2.0.6",
"lowlight": "2.9.0",
"pinia": "2.2.2",
"pinia": "2.1.7",
"register-service-worker": "1.7.2",
"sortablejs": "1.15.3",
"tailwindcss": "3.4.12",
"sortablejs": "1.15.2",
"tailwindcss": "3.4.4",
"tippy.js": "6.3.7",
"ufo": "1.5.4",
"vue": "3.5.6",
"vue": "3.4.32",
"vue-advanced-cropper": "2.8.9",
"vue-flatpickr-component": "11.0.5",
"vue-i18n": "10.0.1",
"vue-router": "4.4.5",
"vue-i18n": "9.13.1",
"vue-router": "4.4.0",
"vuemoji-picker": "0.2.1",
"workbox-precaching": "7.1.0",
"zhyswan-vuedraggable": "4.1.3"
},
"devDependencies": {
"@4tw/cypress-drag-drop": "2.2.5",
"@cypress/vite-dev-server": "5.2.0",
"@cypress/vite-dev-server": "5.1.1",
"@cypress/vue": "6.0.1",
"@faker-js/faker": "8.4.1",
"@histoire/plugin-screenshot": "0.17.17",
"@histoire/plugin-vue": "0.17.17",
"@rushstack/eslint-patch": "1.10.4",
"@rushstack/eslint-patch": "1.10.3",
"@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.2",
"@types/lodash.clonedeep": "4.5.9",
"@types/node": "20.16.5",
"@types/node": "20.14.11",
"@types/sortablejs": "1.15.8",
"@typescript-eslint/eslint-plugin": "7.18.0",
"@typescript-eslint/parser": "7.18.0",
"@vitejs/plugin-legacy": "5.4.2",
"@vitejs/plugin-vue": "5.1.4",
"@typescript-eslint/eslint-plugin": "7.16.1",
"@typescript-eslint/parser": "7.16.1",
"@vitejs/plugin-legacy": "5.4.1",
"@vitejs/plugin-vue": "5.0.5",
"@vue/eslint-config-typescript": "13.0.0",
"@vue/test-utils": "2.4.6",
"@vue/tsconfig": "0.5.1",
"autoprefixer": "10.4.20",
"browserslist": "4.23.3",
"caniuse-lite": "1.0.30001660",
"autoprefixer": "10.4.19",
"browserslist": "4.23.2",
"caniuse-lite": "1.0.30001642",
"csstype": "3.1.3",
"cypress": "13.14.2",
"esbuild": "0.23.1",
"eslint": "8.57.1",
"eslint-plugin-vue": "9.28.0",
"cypress": "13.13.1",
"esbuild": "0.23.0",
"eslint": "8.57.0",
"eslint-plugin-vue": "9.27.0",
"happy-dom": "14.12.3",
"histoire": "0.17.17",
"postcss": "8.4.47",
"postcss": "8.4.39",
"postcss-easing-gradients": "3.0.1",
"postcss-easings": "4.0.0",
"postcss-preset-env": "9.6.0",
"rollup": "4.21.3",
"rollup": "4.18.1",
"rollup-plugin-visualizer": "5.12.0",
"sass": "1.79.1",
"start-server-and-test": "2.0.8",
"typescript": "5.6.2",
"unplugin-inject-preload": "2.0.5",
"vite": "5.4.6",
"vite-plugin-pwa": "0.20.5",
"sass": "1.77.8",
"start-server-and-test": "2.0.4",
"typescript": "5.5.3",
"unplugin-inject-preload": "2.0.4",
"vite": "5.3.4",
"vite-plugin-pwa": "0.20.0",
"vite-plugin-sentry": "1.4.0",
"vite-svg-loader": "5.1.0",
"vitest": "1.6.0",
"vue-tsc": "2.1.6",
"vue-tsc": "2.0.26",
"wait-on": "7.2.0",
"workbox-cli": "7.1.0"
},
"pnpm": {
"patchedDependencies": {
"@github/hotkey@3.1.1": "patches/@github__hotkey@3.1.1.patch",
"flexsearch@0.7.43": "patches/flexsearch@0.7.43.patch"
"flexsearch@0.7.31": "patches/flexsearch@0.7.31.patch",
"@github/hotkey@3.1.1": "patches/@github__hotkey@3.1.1.patch"
}
}
}
+16
View File
@@ -0,0 +1,16 @@
diff --git a/index.d.ts b/index.d.ts
deleted file mode 100644
index 9f39f41073864b83968bdaa242ac4e3c3149685a..0000000000000000000000000000000000000000
diff --git a/package.json b/package.json
index 8968f5bf8010ff194240591c8b83299f7328e79d..6d84b6f590a841b129ed8b3860cb786df5a185c0 100644
--- a/package.json
+++ b/package.json
@@ -22,8 +22,6 @@
},
"main": "dist/flexsearch.bundle.js",
"browser": "dist/flexsearch.bundle.js",
- "module": "dist/module/index.js",
- "types": "./index.d.ts",
"preferGlobal": false,
"repository": {
"type": "git",
-18
View File
@@ -1,18 +0,0 @@
diff --git a/package.json b/package.json
index c154e54029c94be444916fb2249941e7182d80ed..54a65c42a42c4627506e016132becc43b47a517c 100644
--- a/package.json
+++ b/package.json
@@ -28,13 +28,11 @@
"email": "info@nextapps.de"
},
"main": "dist/flexsearch.bundle.min.js",
- "module": "dist/flexsearch.bundle.module.min.js",
"browser": {
"dist/flexsearch.bundle.min.js": "./dist/flexsearch.bundle.min.js",
"dist/flexsearch.bundle.module.min.js": "./dist/flexsearch.bundle.module.min.js",
"worker_threads": false
},
- "types": "./index.d.ts",
"scripts": {
"build": "npm run copy && npm run build:bundle",
"build:bundle": "node task/build RELEASE=bundle DEBUG=false SUPPORT_WORKER=true SUPPORT_ENCODER=true SUPPORT_CACHE=true SUPPORT_ASYNC=true SUPPORT_STORE=true SUPPORT_TAGS=true SUPPORT_SUGGESTION=true SUPPORT_SERIALIZE=true SUPPORT_DOCUMENT=true POLYFILL=false",
+2374 -2543
View File
File diff suppressed because it is too large Load Diff
@@ -69,10 +69,6 @@ const shouldShowMessage = computed(() => {
@media screen and (min-width: $tablet) {
display: none;
}
@media print {
display: none;
}
&.has-update-available {
bottom: 5rem;
+1 -4
View File
@@ -48,10 +48,7 @@
class="task-detail-view-modal"
@close="closeModal()"
>
<component
:is="currentModal"
@close="closeModal()"
/>
<component :is="currentModal" />
</Modal>
<BaseButton
@@ -3,7 +3,7 @@
class="list-menu loader-container is-loading-small"
:class="{'is-loading': isLoading}"
>
<div class="navigation-item">
<div>
<BaseButton
v-if="canCollapse && childProjects?.length > 0"
class="collapse-project-button"
@@ -193,18 +193,4 @@ const childProjects = computed(() => {
.is-touch .handle.has-color-bubble {
display: none !important;
}
.navigation-item:has(*:focus-visible) {
box-shadow: 0 0 0 2px hsla(var(--primary-hsl), 0.5);
background-color: var(--white);
.favorite, .menu-list-dropdown {
opacity: 1;
}
}
.navigation-item a:focus-visible {
// The focus ring is already added to the navigation-item, so we don't need to add it again.
box-shadow: none;
}
</style>
+4 -22
View File
@@ -2,9 +2,8 @@
<div
ref="multiselectRoot"
class="multiselect"
:class="{'has-search-results': searchResultsVisible, 'is-disabled': disabled}"
:class="{'has-search-results': searchResultsVisible}"
tabindex="-1"
:aria-disabled="disabled"
@focus="focus"
>
<div
@@ -13,7 +12,7 @@
>
<div
class="input-wrapper input"
:class="{'has-multiple': hasMultiple, 'has-removal-button': removalAvailable && !disabled}"
:class="{'has-multiple': hasMultiple, 'has-removal-button': removalAvailable}"
>
<slot
v-if="Array.isArray(internalValue)"
@@ -32,7 +31,6 @@
>
{{ label !== '' ? item[label] : item }}
<BaseButton
v-if="!disabled"
class="delete is-small"
@click="() => remove(item)"
/>
@@ -42,7 +40,6 @@
</slot>
<input
v-if="!disabled"
:id="id"
ref="searchInput"
v-model="query"
@@ -58,7 +55,7 @@
@focus="handleFocus"
>
<BaseButton
v-if="removalAvailable && !disabled"
v-if="removalAvailable"
class="removal-button"
@click="resetSelectedValue"
>
@@ -154,7 +151,7 @@ const props = withDefaults(defineProps<{
/** The text shown next to the new value option. */
createPlaceholder?: string
/** The text shown next to an option. */
selectPlaceholder?: string
selectPlaceholder: string
/** If true, allows for selecting multiple items. v-model will be an array with all selected values in that case. */
multiple?: boolean
/** If true, displays the search results inline instead of using a dropdown. */
@@ -167,8 +164,6 @@ const props = withDefaults(defineProps<{
closeAfterSelect?: boolean
/** If false, the search input will get the autocomplete="off" attributes attached to it. */
autocompleteEnabled?: boolean
/** If true, disables the multiselect input */
disabled?: boolean
}>(), {
modelValue: null,
loading: false,
@@ -184,7 +179,6 @@ const props = withDefaults(defineProps<{
searchDelay: 200,
closeAfterSelect: true,
autocompleteEnabled: true,
disabled: false,
})
const emit = defineEmits<{
@@ -454,18 +448,6 @@ function focus() {
.control.is-loading::after {
top: .75rem;
}
&.is-disabled {
.input-wrapper, .input, & {
cursor: default !important;
&:focus-within,&:focus-visible, &:hover, &:focus {
border-color: transparent !important;
background: transparent !important;
box-shadow: none;
}
}
}
}
.input-wrapper {
+16 -3
View File
@@ -36,7 +36,6 @@ import {ref, watchEffect} from 'vue'
import {useDebounceFn} from '@vueuse/core'
import {useI18n} from 'vue-i18n'
import BaseButton from '@/components/base/BaseButton.vue'
import {validatePassword} from '@/helpers/validatePasswort'
const props = withDefaults(defineProps<{
modelValue: string,
@@ -61,8 +60,22 @@ const validateAfterFirst = ref(false)
watchEffect(() => props.validateInitially && validate())
const validate = useDebounceFn(() => {
const valid = validatePassword(password.value, props.validateMinLength)
isValid.value = valid === true ? true : t(valid)
if (password.value === '') {
isValid.value = t('user.auth.passwordRequired')
return
}
if (props.validateMinLength && password.value.length < 8) {
isValid.value = t('user.auth.passwordNotMin')
return
}
if (props.validateMinLength && password.value.length > 250) {
isValid.value = t('user.auth.passwordNotMax')
return
}
isValid.value = true
}, 100)
function togglePasswordFieldType() {
@@ -314,17 +314,7 @@ const internalMode = ref<Mode>('preview')
const isEditing = computed(() => internalMode.value === 'edit' && isEditEnabled)
const contentHasChanged = ref<boolean>(false)
let lastSavedState = ''
watch(
() => modelValue,
(newValue) => {
if (!contentHasChanged.value) {
lastSavedState = newValue
}
},
{ immediate: true },
)
let lastSavedState = modelValue
watch(
() => internalMode.value,
+17 -52
View File
@@ -20,12 +20,6 @@
class="modal-container"
@mousedown.self.prevent.stop="$emit('close')"
>
<BaseButton
class="close"
@click="$emit('close')"
>
<Icon icon="times" />
</BaseButton>
<div
class="modal-content"
:class="{
@@ -33,6 +27,13 @@
'is-wide': wide
}"
>
<BaseButton
class="close"
@click="$emit('close')"
>
<Icon icon="times" />
</BaseButton>
<slot>
<div class="header">
<slot name="header" />
@@ -136,8 +137,8 @@ $modal-width: 1024px;
@media screen and (max-width: $tablet) {
margin: 0;
position: static;
transform: none;
top: 25%;
transform: translate(-50%, -25%);
}
.header {
@@ -153,15 +154,12 @@ $modal-width: 1024px;
// scrolling-content
// used e.g. for <TaskDetailViewModal>
.scrolling .modal-content {
max-width: $modal-width;
width: 100%;
margin: $modal-margin auto;
max-height: none; // reset bulma
overflow: visible; // reset bulma
@media not print {
max-width: $modal-width;
}
@media screen and (min-width: $tablet) {
max-height: none; // reset bulma
@@ -169,7 +167,7 @@ $modal-width: 1024px;
width: 100%;
}
@media screen and (max-width: $desktop), print {
@media screen and (max-width: $desktop) {
margin: 0;
}
}
@@ -196,7 +194,7 @@ $modal-width: 1024px;
position: fixed;
top: .5rem;
right: $close-button-padding;
color: var(--white);
color: var(--grey-900);
font-size: 2rem;
@media screen and (min-width: $desktop) and (max-width: calc(#{$desktop } + #{$close-button-min-space})) {
@@ -205,42 +203,15 @@ $modal-width: 1024px;
// we align the close button to the modal until there is enough space outside for it
transform: translateX(calc((#{$modal-width} / 2) - #{$close-button-padding}));
}
// we can only use light color when there is enough space for the close button next to the modal
@media screen and (min-width: calc(#{$desktop } + #{$close-button-min-space})) {
color: var(--white);
}
@media screen and (min-width: $tablet) and (max-width: #{$desktop + $close-button-min-space}) {
top: .75rem;
}
}
@media print, screen and (max-width: $tablet) {
.modal-mask {
position: static;
overflow: visible !important;
}
.modal-container {
height: auto;
min-height: 100vh;
}
.modal-content {
position: static;
max-height: none;
}
.close {
display: none;
}
:deep(.card) {
border: none !important;
border-radius: 0 !important;
min-height: 100vh;
display: flex;
flex-direction: column;
justify-content: space-between;
margin-bottom: 0 !important;
}
}
</style>
<style lang="scss">
@@ -248,10 +219,4 @@ $modal-width: 1024px;
.dark .close {
color: var(--grey-900);
}
@media print, screen and (max-width: $tablet) {
body:has(.modal-mask) #app {
display: none;
}
}
</style>
</style>
@@ -218,35 +218,33 @@ function handleFieldInput() {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [matched, prefix, operator, space, keyword] = match
if(!keyword) {
return
}
let search = keyword
if (operator === 'in' || operator === '?=') {
const keywords = keyword.split(',')
search = keywords[keywords.length - 1].trim()
}
if (matched.startsWith('label')) {
autocompleteResultType.value = 'labels'
autocompleteResults.value = labelStore.filterLabelsByQuery([], search)
}
if (matched.startsWith('assignee')) {
autocompleteResultType.value = 'assignees'
if (props.projectId) {
projectUserService.getAll({projectId: props.projectId}, {s: search})
.then(users => autocompleteResults.value = users.length > 1 ? users : [])
} else {
userService.getAll({}, {s: search})
.then(users => autocompleteResults.value = users.length > 1 ? users : [])
if (keyword) {
let search = keyword
if (operator === 'in' || operator === '?=') {
const keywords = keyword.split(',')
search = keywords[keywords.length - 1].trim()
}
if (matched.startsWith('label')) {
autocompleteResultType.value = 'labels'
autocompleteResults.value = labelStore.filterLabelsByQuery([], search)
}
if (matched.startsWith('assignee')) {
autocompleteResultType.value = 'assignees'
if (props.projectId) {
projectUserService.getAll({projectId: props.projectId}, {s: search})
.then(users => autocompleteResults.value = users.length > 1 ? users : [])
} else {
userService.getAll({}, {s: search})
.then(users => autocompleteResults.value = users.length > 1 ? users : [])
}
}
if (!props.projectId && matched.startsWith('project')) {
autocompleteResultType.value = 'projects'
autocompleteResults.value = projectStore.searchProject(search)
}
autocompleteMatchText.value = keyword
autocompleteMatchPosition.value = match.index + prefix.length - 1 + keyword.replace(search, '').length
}
if (!props.projectId && matched.startsWith('project')) {
autocompleteResultType.value = 'projects'
autocompleteResults.value = projectStore.searchProject(search)
}
autocompleteMatchText.value = keyword
autocompleteMatchPosition.value = match.index + prefix.length - 1 + keyword.replace(search, '').length
})
}
@@ -112,7 +112,7 @@ watch(
const val = {...value}
val.filter = transformFilterStringFromApi(
val?.filter || '',
labelId => labelStore.getLabelById(labelId)?.title || null,
labelId => labelStore.getLabelById(labelId)?.title,
projectId => projectStore.projects[projectId]?.title || null,
)
params.value = val
@@ -35,7 +35,7 @@
<BaseButton
class="project-button"
:aria-label="project.title"
:title="textOnlyDescription"
:title="project.description"
:to="{
name: 'project.index',
params: { projectId: project.id}
@@ -53,7 +53,6 @@
</template>
<script lang="ts" setup>
import {computed} from 'vue'
import type {IProject} from '@/modelTypes/IProject'
import BaseButton from '@/components/base/BaseButton.vue'
@@ -69,10 +68,6 @@ const props = defineProps<{
const {background, blurHashUrl} = useProjectBackground(() => props.project)
const projectStore = useProjectStore()
const textOnlyDescription = computed(() => {
return props.project.description ? props.project.description.replace(/<[^>]*>/g, '') : ''
})
</script>
<style lang="scss" scoped>
@@ -1,8 +1,5 @@
<template>
<ul
class="project-grid"
:class="{ 'show-even-number-of-projects': showEvenNumberOfProjects }"
>
<ul class="project-grid">
<li
v-for="(item, index) in filteredProjects"
:key="`project_${item.id}_${index}`"
@@ -22,13 +19,11 @@ import ProjectCard from './ProjectCard.vue'
const props = withDefaults(defineProps<{
projects: IProject[],
showArchived?: boolean,
itemLimit?: boolean,
showEvenNumberOfProjects?: boolean,
itemLimit?: boolean
}>(), {
projects: () => [],
showArchived: false,
itemLimit: false,
showEvenNumberOfProjects: false,
})
const filteredProjects = computed(() => {
@@ -63,13 +58,9 @@ const filteredProjects = computed(() => {
@media screen and (min-width: $widescreen) {
--project-grid-columns: 5;
}
&.show-even-number-of-projects {
@media screen and (min-width: $widescreen) {
.project-grid-item:nth-child(5) {
display: none;
}
.project-grid-item:nth-child(6) {
display: none;
}
}
}
@@ -41,7 +41,7 @@
@click="() => unCollapseBucket(bucket)"
>
<span
v-if="bucket.id !== 0 && view?.doneBucketId === bucket.id"
v-if="view?.doneBucketId === bucket.id"
v-tooltip="$t('project.kanban.doneBucketHint')"
class="icon is-small has-text-success mr-2"
>
@@ -512,7 +512,6 @@ async function updateTaskPosition(e) {
taskId: newTask.id,
})
await taskPositionService.value.update(newPosition)
newTask.position = position
if(bucketHasChanged) {
const updatedTaskBucket = await taskBucketService.value.update(new TaskBucketModel({
@@ -525,8 +524,8 @@ async function updateTaskPosition(e) {
if (updatedTaskBucket.bucketId !== newTask.bucketId) {
kanbanStore.moveTaskToBucket(newTask, updatedTaskBucket.bucketId)
}
kanbanStore.setTaskInBucket(newTask)
}
kanbanStore.setTaskInBucket(newTask)
// Make sure the first and second task don't both get position 0 assigned
if (newTaskIndex === 0 && taskAfter !== null && taskAfter.position === 0) {
@@ -547,9 +546,6 @@ async function updateTaskPosition(e) {
}
function toggleShowNewTaskInput(bucketId: IBucket['id']) {
if (loading.value || taskLoading.value) {
return
}
showNewTaskInput.value[bucketId] = !showNewTaskInput.value[bucketId]
newTaskInputFocused.value = false
}
@@ -566,18 +562,17 @@ async function addTaskToBucket(bucketId: IBucket['id']) {
bucketId,
projectId: project.value.id,
})
toggleShowNewTaskInput(bucketId)
newTaskText.value = ''
kanbanStore.addTaskToBucket(task)
scrollTaskContainerToTop(bucketId)
scrollTaskContainerToBottom(bucketId)
}
function scrollTaskContainerToTop(bucketId: IBucket['id']) {
function scrollTaskContainerToBottom(bucketId: IBucket['id']) {
const bucketEl = taskContainerRefs.value[bucketId]
if (!bucketEl) {
return
}
bucketEl.scrollTop = 0
bucketEl.scrollTop = bucketEl.scrollHeight
}
async function createNewBucket() {
@@ -771,18 +766,6 @@ function unCollapseBucket(bucket: IBucket) {
}
</script>
<style lang="scss" scoped>
.control.is-loading {
&::after {
top: 30%;
right: 50%;
transform: translate(-50%, 0);
--loader-border-color: var(--grey-500);
}
}
</style>
<style lang="scss">
$ease-out: all .3s cubic-bezier(0.23, 1, 0.32, 1);
$bucket-width: 300px;
@@ -805,9 +788,8 @@ $filter-container-height: '1rem - #{$switch-view-height}';
}
@media screen and (max-width: $tablet) {
height: calc(#{$crazy-height-calculation} - #{$filter-container-height} + 9px);
height: calc(#{$crazy-height-calculation} - #{$filter-container-height});
scroll-snap-type: x mandatory;
margin: 0 -0.5rem;
}
&-bucket-container {
@@ -885,6 +867,7 @@ $filter-container-height: '1rem - #{$switch-view-height}';
// to hide the fact we just made the button smaller.
min-width: calc(#{$bucket-width} + 1rem);
background: transparent;
padding-right: 1rem;
.button {
background: var(--grey-100);
@@ -45,6 +45,7 @@
<draggable
v-if="tasks && tasks.length > 0"
v-bind="DRAG_OPTIONS"
v-model="tasks"
group="tasks"
handle=".handle"
@@ -58,8 +59,6 @@
},
type: 'transition-group'
}"
:animation="100"
ghost-class="task-ghost"
@start="() => drag = true"
@end="saveTaskPosition"
>
@@ -129,6 +128,10 @@ const props = defineProps<{
const ctaVisible = ref(false)
const drag = ref(false)
const DRAG_OPTIONS = {
animation: 100,
ghostClass: 'task-ghost',
} as const
const {
tasks: allTasks,
@@ -289,7 +289,6 @@ import type {ITask} from '@/modelTypes/ITask'
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'
const props = defineProps<{
projectId: IProject['id'],
@@ -356,7 +355,7 @@ function sort(property: keyof SortBy) {
function setActiveColumnsSortParam() {
sortByParam.value = Object.keys(sortBy.value)
.filter(prop => activeColumns.value[camelCase(prop)])
.filter(prop => activeColumns.value[prop])
.reduce((obj, key) => {
obj[key] = sortBy.value[key]
return obj
@@ -30,7 +30,7 @@ const projectStore = useProjectStore()
onBeforeMount(() => {
const transform = (filterString: string) => transformFilterStringFromApi(
filterString,
labelId => labelStore.getLabelById(labelId)?.title || null,
labelId => labelStore.getLabelById(labelId)?.title,
projectId => projectStore.projects[projectId]?.title || null,
)
@@ -51,7 +51,7 @@ onBeforeMount(() => {
function save() {
const transformFilter = (filterQuery: string) => transformFilterStringForApi(
filterQuery,
labelTitle => labelStore.getLabelByExactTitle(labelTitle)?.id || null,
labelTitle => labelStore.filterLabelsByQuery([], labelTitle)[0]?.id || null,
projectTitle => {
const found = projectStore.findProjectByExactname(projectTitle)
return found?.id || null
+4 -42
View File
@@ -51,7 +51,6 @@
import {computed, ref} from 'vue'
import {useI18n} from 'vue-i18n'
import {useElementHover} from '@vueuse/core'
import {useRouter} from 'vue-router'
import {RELATION_KIND} from '@/types/IRelationKind'
import type {ITask} from '@/modelTypes/ITask'
@@ -67,8 +66,6 @@ import {useAuthStore} from '@/stores/auth'
import {useTaskStore} from '@/stores/tasks'
import {useAutoHeightTextarea} from '@/composables/useAutoHeightTextarea'
import TaskService from '@/services/task'
import TaskModel from '@/models/task'
const props = withDefaults(defineProps<{
defaultPosition?: number,
@@ -84,7 +81,6 @@ const {textarea: newTaskInput} = useAutoHeightTextarea(newTaskTitle)
const {t} = useI18n({useScope: 'global'})
const authStore = useAuthStore()
const taskStore = useTaskStore()
const router = useRouter()
// enable only if we don't have a modal
// onStartTyping(() => {
@@ -133,55 +129,21 @@ async function addTask() {
const allLabels = tasksToCreate.map(({title}) => getLabelsFromPrefix(title, authStore.settings.frontendSettings.quickAddMagicMode) ?? [])
await taskStore.ensureLabelsExist(allLabels.flat())
const taskCollectionService = new TaskService()
const projectIndices = new Map<number, number>()
let currentProjectId = authStore.settings.defaultProjectId
if (typeof router.currentRoute.value.params.projectId !== 'undefined') {
currentProjectId = Number(router.currentRoute.value.params.projectId)
}
// Create a map of project indices before creating tasks
if (tasksToCreate.length > 1) {
for (const {project} of tasksToCreate) {
const projectId = project !== null
? await taskStore.findProjectId({project, projectId: 0})
: currentProjectId
if (!projectIndices.has(projectId)) {
const newestTask = await taskCollectionService.getAll(new TaskModel({}), {
sort_by: ['id'],
order_by: ['desc'],
per_page: 1,
filter: `project_id = ${projectId}`,
})
projectIndices.set(projectId, newestTask[0]?.index || 0)
}
}
}
const newTasks = tasksToCreate.map(async ({title, project}, index) => {
const newTasks = tasksToCreate.map(async ({title, project}) => {
if (title === '') {
return
}
// If the task has a project specified, make sure to use it
const projectId = project !== null
? await taskStore.findProjectId({project, projectId: 0})
: currentProjectId
// Calculate new index for this task per project
let taskIndex: number | undefined
if (tasksToCreate.length > 1) {
const lastIndex = projectIndices.get(projectId)
taskIndex = lastIndex + index + 1
let projectId = null
if (project !== null) {
projectId = await taskStore.findProjectId({project, projectId: 0})
}
const task = await taskStore.createNewTask({
title,
projectId: projectId || authStore.settings.defaultProjectId,
position: props.defaultPosition,
index: taskIndex,
})
createdTasks[title] = task
return task
@@ -180,7 +180,7 @@ import ProgressBar from '@/components/misc/ProgressBar.vue'
import BaseButton from '@/components/base/BaseButton.vue'
import AttachmentService from '@/services/attachment'
import {canPreview} from '@/models/attachment'
import {SUPPORTED_IMAGE_SUFFIX} from '@/models/attachment'
import type {IAttachment} from '@/modelTypes/IAttachment'
import type {ITask} from '@/modelTypes/ITask'
@@ -274,6 +274,10 @@ async function viewOrDownload(attachment: IAttachment) {
}
}
function canPreview(attachment: IAttachment): boolean {
return SUPPORTED_IMAGE_SUFFIX.some((suffix) => attachment.file.name.toLowerCase().endsWith(suffix))
}
const copy = useCopyToClipboard()
function copyUrl(attachment: IAttachment) {
@@ -22,7 +22,6 @@
</span>
<div
v-for="c in comments"
:id="`comment-${c.id}`"
:key="c.id"
class="media comment"
>
@@ -57,13 +56,6 @@
>
· {{ $t('task.comment.edited', {date: formatDateSince(c.updated)}) }}
</span>
<a
v-tooltip="$t('task.comment.permalink')"
:href="`#comment-${c.id}`"
class="comment-permalink"
:title="$t('task.comment.permalink')"
@click.prevent.stop="copy(getCommentUrl(`${c.id}`))"
><Icon icon="link" /></a>
<CustomTransition name="fade">
<span
v-if="
@@ -207,7 +199,6 @@ import {getAvatarUrl, getDisplayName} from '@/models/user'
import {useConfigStore} from '@/stores/config'
import {useAuthStore} from '@/stores/auth'
import Reactions from '@/components/input/Reactions.vue'
import { useCopyToClipboard } from '@/composables/useCopyToClipboard'
const props = withDefaults(defineProps<{
taskId: number,
@@ -216,8 +207,6 @@ const props = withDefaults(defineProps<{
canWrite: true,
})
const copy = useCopyToClipboard()
const {t} = useI18n({useScope: 'global'})
const configStore = useConfigStore()
const authStore = useAuthStore()
@@ -253,8 +242,6 @@ const actions = computed(() => {
])))
})
const frontendUrl = computed(() => configStore.frontendUrl)
async function attachmentUpload(files: File[] | FileList): (Promise<string[]>) {
const uploadPromises: Promise<string>[] = []
@@ -377,10 +364,6 @@ async function deleteComment(commentToDelete: ITaskComment) {
showDeleteModal.value = false
}
}
function getCommentUrl(commentId: string) {
return `${frontendUrl.value}${location.pathname}${location.search}#comment-${commentId}`
}
</script>
<style lang="scss" scoped>
@@ -408,23 +391,10 @@ function getCommentUrl(commentId: string) {
}
span,
.comment-permalink {
span {
font-size: .75rem;
line-height: 1;
}
.comment-permalink {
font-size: 1rem;
border: 1px solid transparent;
padding: 0.25rem;
border-radius: 1rem;
color: var(--grey, hsl(0, 0%, 48%));
}
.comment-permalink:hover {
color: var(--grey-dark, hsl(0, 0%, 29%));
border-color: var(--grey-dark, hsl(0, 0%, 29%));
}
}
.image.is-avatar {
@@ -135,8 +135,18 @@ async function updateDueDate() {
$defer-task-max-width: 350px + 100px;
.defer-task {
position: absolute;
width: 100%;
max-width: $defer-task-max-width;
border-radius: $radius;
border: 1px solid var(--grey-200);
padding: 1rem;
margin: 1rem;
background: var(--white);
color: var(--text);
cursor: default;
z-index: 10;
box-shadow: var(--shadow-lg);
@media screen and (max-width: ($defer-task-max-width)) {
left: .5rem;
@@ -10,7 +10,6 @@
:create-placeholder="$t('task.label.createPlaceholder')"
:search-delay="10"
:close-after-select="false"
:disabled="disabled"
@search="findLabel"
@select="addLabel"
@create="createAndAddLabel"
@@ -22,7 +21,6 @@
>
<span>{{ label.title }}</span>
<BaseButton
v-if="!disabled"
v-cy="'taskDetail.removeLabel'"
class="delete is-small"
@click="removeLabel(label)"
@@ -20,9 +20,9 @@
<script setup lang="ts">
import {ref, shallowReactive, watchEffect} from 'vue'
import AttachmentService, {PREVIEW_SIZE} from '@/services/attachment'
import AttachmentService from '@/services/attachment'
import type {IAttachment} from '@/modelTypes/IAttachment'
import {canPreview} from '@/models/attachment'
import {SUPPORTED_IMAGE_SUFFIX} from '@/models/attachment'
const props = defineProps<{
modelValue?: IAttachment
@@ -33,9 +33,13 @@ const blobUrl = ref<string | undefined>(undefined)
watchEffect(async () => {
if (props.modelValue && canPreview(props.modelValue)) {
blobUrl.value = await attachmentService.getBlobUrl(props.modelValue, PREVIEW_SIZE.MD)
blobUrl.value = await attachmentService.getBlobUrl(props.modelValue)
}
})
function canPreview(attachment: IAttachment): boolean {
return SUPPORTED_IMAGE_SUFFIX.some((suffix) => attachment.file.name.toLowerCase().endsWith(suffix))
}
</script>
<style scoped lang="scss">
@@ -1,26 +1,20 @@
<template>
<div class="heading">
<div class="tw-flex tw-items-center md:tw-items-stretch tw-flex-col tw-gap-1 task-properties">
<div class="tw-flex tw-items-center tw-gap-2">
<ColorBubble
v-if="task.hexColor !== ''"
:color="getHexColor(task.hexColor)"
/>
<BaseButton @click="copyUrl">
<h1 class="title task-id">
{{ textIdentifier }}
</h1>
</BaseButton>
</div>
<div class="flex is-align-items-center">
<BaseButton @click="copyUrl">
<h1 class="title task-id">
{{ textIdentifier }}
</h1>
</BaseButton>
<Done
class="heading__done"
:is-done="task.done"
/>
<BaseButton
class="close"
@click="$emit('close')"
>
<Icon icon="times" />
</BaseButton>
<ColorBubble
v-if="task.hexColor !== ''"
:color="getHexColor(task.hexColor)"
class="ml-2"
/>
</div>
<h1
class="title input"
@@ -32,12 +26,6 @@
>
{{ task.title.trim() }}
</h1>
<BaseButton
class="close"
@click="$emit('close')"
>
<Icon icon="times" />
</BaseButton>
<CustomTransition name="fade">
<span
v-if="loading && saving"
@@ -83,8 +71,7 @@ const props = withDefaults(defineProps<{
})
const emit = defineEmits<{
'update:task': [task: ITask],
'close': [],
'update:task': [task: ITask]
}>()
const router = useRouter()
@@ -151,11 +138,14 @@ async function save(title: string) {
.title.input {
// 1.8rem is the font-size, 1.125 is the line-height, .3rem padding everywhere, 1px border around the whole thing.
min-height: calc(1.8rem * 1.125 + .6rem + 2px);
margin-right: 0;
@media screen and (max-width: $tablet) {
margin: 0 -.3rem .5rem -.3rem; // the title has 0.3rem padding - this make the text inside of it align with the rest
}
@media screen and (min-width: $tablet) and (max-width: #{$desktop + $close-button-min-space}) {
width: calc(100% - 6.5rem);
}
}
.title.task-id {
@@ -163,39 +153,12 @@ async function save(title: string) {
white-space: nowrap;
}
.heading__done {
margin-left: .5rem;
}
.color-bubble {
height: .75rem;
width: .75rem;
}
.close {
font-size: 2rem;
margin-left: 0.5rem;
line-height: 1;
@media screen and (max-width: $tablet) {
display: none;
}
@media screen and (min-width: #{$desktop + 1px}) {
display: none;
}
}
.task-properties .close {
display: none;
position: absolute;
right: 1.25rem;
top: 1.1rem;
@media screen and (max-width: $tablet) {
display: block;
}
}
.task-properties {
@media screen and (max-width: $tablet) {
flex-direction: row;
}
}
</style>
@@ -93,7 +93,7 @@
</template>
<script lang="ts" setup>
import {computed, ref, watch} from 'vue'
import {ref, computed, watch} from 'vue'
import {useRouter} from 'vue-router'
import PriorityLabel from '@/components/tasks/partials/PriorityLabel.vue'
@@ -105,9 +105,9 @@ import ChecklistSummary from './ChecklistSummary.vue'
import {getHexColor} from '@/models/task'
import type {ITask} from '@/modelTypes/ITask'
import {SUPPORTED_IMAGE_SUFFIX} from '@/models/attachment'
import AttachmentService, {PREVIEW_SIZE} from '@/services/attachment'
import AttachmentService from '@/services/attachment'
import {formatDateLong, formatDateSince, formatISO} from '@/helpers/time/formatDate'
import {formatDateLong, formatISO, formatDateSince} from '@/helpers/time/formatDate'
import {colorIsDark} from '@/helpers/color/colorIsDark'
import {useTaskStore} from '@/stores/tasks'
import AssigneeList from '@/components/tasks/partials/AssigneeList.vue'
@@ -165,7 +165,7 @@ async function maybeDownloadCoverImage() {
}
const attachmentService = new AttachmentService()
coverImageBlobUrl.value = await attachmentService.getBlobUrl(attachment, PREVIEW_SIZE.LG)
coverImageBlobUrl.value = await attachmentService.getBlobUrl(attachment)
}
watch(
@@ -123,7 +123,7 @@
<FancyCheckbox
v-model="task.done"
class="task-done-checkbox"
@update:modelValue="toggleTaskDone(task)"
@update:modelValue="toggleTaskDone(t)"
/>
<RouterLink
:to="{ name: route.name as string, params: { id: task.id } }"
@@ -1,17 +1,17 @@
<template>
<div>
<div
ref="taskContainerRef"
:class="{'is-loading': taskService.loading}"
class="task loader-container single-task"
tabindex="-1"
@click="openTaskDetail"
@keyup.enter="openTaskDetail"
@mouseup.stop.self="openTaskDetail"
@mousedown.stop.self="focusTaskLink"
>
<FancyCheckbox
v-model="task.done"
:disabled="(isArchived || disabled) && !canMarkAsDone"
@update:modelValue="markAsDone"
@click.stop
/>
<ColorBubble
@@ -23,6 +23,8 @@
<div
:class="{ 'done': task.done, 'show-project': showProject && project}"
class="tasktext"
@mouseup.stop.self="openTaskDetail"
@mousedown.stop.self="focusTaskLink"
>
<span>
<RouterLink
@@ -31,7 +33,6 @@
:to="{ name: 'project.index', params: { projectId: task.projectId } }"
class="task-project mr-1"
:class="{'mr-2': task.hexColor !== ''}"
@click.stop
>
{{ project.title }}
</RouterLink>
@@ -41,15 +42,15 @@
:color="getHexColor(task.hexColor)"
class="mr-1"
/>
<PriorityLabel
:priority="task.priority"
:done="task.done"
class="pr-2"
/>
<RouterLink
ref="taskLinkRef"
ref="taskLink"
:to="taskDetailRoute"
class="task-link"
tabindex="-1"
@@ -72,32 +73,29 @@
:inline="true"
/>
<Popup
<!-- FIXME: use popup -->
<BaseButton
v-if="+new Date(task.dueDate) > 0"
v-tooltip="formatDateLong(task.dueDate)"
class="dueDate"
@click.prevent.stop="showDefer = !showDefer"
>
<template #trigger="{toggle, isOpen}">
<BaseButton
v-tooltip="formatDateLong(task.dueDate)"
class="dueDate"
@click.prevent.stop="toggle()"
>
<time
:datetime="formatISO(task.dueDate)"
:class="{'overdue': task.dueDate <= new Date() && !task.done}"
class="is-italic"
:aria-expanded="isOpen ? 'true' : 'false'"
>
– {{ $t('task.detail.due', {at: dueDateFormatted}) }}
</time>
</BaseButton>
</template>
<template #content>
<DeferTask
v-model="task"
/>
</template>
</Popup>
<time
:datetime="formatISO(task.dueDate)"
:class="{'overdue': task.dueDate <= new Date() && !task.done}"
class="is-italic"
:aria-expanded="showDefer ? 'true' : 'false'"
>
– {{ $t('task.detail.due', {at: dueDateFormatted}) }}
</time>
</BaseButton>
<CustomTransition name="fade">
<DeferTask
v-if="+new Date(task.dueDate) > 0 && showDefer"
ref="deferDueDate"
v-model="task"
/>
</CustomTransition>
<span>
<span
@@ -140,7 +138,6 @@
v-tooltip="$t('task.detail.belongsToProject', {project: project.title})"
:to="{ name: 'project.index', params: { projectId: task.projectId } }"
class="task-project"
@click.stop
>
{{ project.title }}
</RouterLink>
@@ -148,7 +145,7 @@
<BaseButton
:class="{'is-favorite': task.isFavorite}"
class="favorite"
@click.stop="toggleFavorite"
@click="toggleFavorite"
>
<Icon
v-if="task.isFavorite"
@@ -179,7 +176,7 @@
</template>
<script setup lang="ts">
import {ref, watch, shallowReactive, onMounted, computed} from 'vue'
import {ref, watch, shallowReactive, onMounted, onBeforeUnmount, computed} from 'vue'
import {useI18n} from 'vue-i18n'
import TaskModel, {getHexColor} from '@/models/task'
@@ -194,10 +191,11 @@ import ProgressBar from '@/components/misc/ProgressBar.vue'
import BaseButton from '@/components/base/BaseButton.vue'
import FancyCheckbox from '@/components/input/FancyCheckbox.vue'
import ColorBubble from '@/components/misc/ColorBubble.vue'
import Popup from '@/components/misc/Popup.vue'
import CustomTransition from '@/components/misc/CustomTransition.vue'
import TaskService from '@/services/task'
import {closeWhenClickedOutside} from '@/helpers/closeWhenClickedOutside'
import {formatDateSince, formatISO, formatDateLong} from '@/helpers/time/formatDate'
import {success} from '@/message'
@@ -241,6 +239,7 @@ const {t} = useI18n({useScope: 'global'})
const taskService = shallowReactive(new TaskService())
const task = ref<ITask>(new TaskModel())
const showDefer = ref(false)
const isRepeating = computed(() => task.value.repeatAfter.amount > 0 || (task.value.repeatAfter.amount === 0 && task.value.repeatMode === TASK_REPEAT_MODES.REPEAT_MODE_MONTH))
@@ -255,6 +254,14 @@ watch(
},
)
onMounted(() => {
document.addEventListener('click', hideDeferDueDatePopup)
})
onBeforeUnmount(() => {
document.removeEventListener('click', hideDeferDueDatePopup)
})
const baseStore = useBaseStore()
const projectStore = useProjectStore()
const taskStore = useTaskStore()
@@ -343,22 +350,35 @@ async function toggleFavorite() {
emit('taskUpdated', task.value)
}
const taskLinkRef = ref<HTMLElement | null>(null)
const deferDueDate = ref<typeof DeferTask | null>(null)
function hideDeferDueDatePopup(e) {
if (!showDefer.value) {
return
}
closeWhenClickedOutside(e, deferDueDate.value.$el, () => {
showDefer.value = false
})
}
const taskLink = ref<HTMLElement | null>(null)
const taskContainerRef = ref<HTMLElement | null>(null)
function hasTextSelected() {
const isTextSelected = window.getSelection().toString()
return !(typeof isTextSelected === 'undefined' || isTextSelected === '' || isTextSelected === '\n')
}
function openTaskDetail(event: MouseEvent | KeyboardEvent) {
if (event.target instanceof HTMLElement) {
const isInteractiveElement = event.target.closest('a, button, input, .favorite, [role="button"]')
if (isInteractiveElement || hasTextSelected()) {
return
}
function openTaskDetail() {
if (!hasTextSelected()) {
taskLink.value.$el.click()
}
}
taskLinkRef.value?.$el.click()
function focusTaskLink() {
if (!hasTextSelected()) {
taskContainerRef.value.focus()
}
}
</script>
@@ -377,7 +397,7 @@ function openTaskDetail(event: MouseEvent | KeyboardEvent) {
background-color: var(--grey-100);
}
&:focus-within {
&:focus-within, &:focus {
box-shadow: 0 0 0 2px hsla(var(--primary-hsl), 0.5);
a.task-link {
@@ -385,16 +405,6 @@ function openTaskDetail(event: MouseEvent | KeyboardEvent) {
}
}
@supports not selector(:focus-within) {
:focus {
box-shadow: 0 0 0 2px hsla(var(--primary-hsl), 0.5);
a.task-link {
box-shadow: none;
}
}
}
.tasktext,
&.tasktext {
text-overflow: ellipsis;
@@ -532,17 +542,4 @@ function openTaskDetail(event: MouseEvent | KeyboardEvent) {
.subtask-nested {
margin-left: 1.75rem;
}
:deep(.popup) {
border-radius: $radius;
background-color: var(--white);
box-shadow: var(--shadow-lg);
color: var(--text);
top: unset;
&.is-open {
padding: 1rem;
border: 1px solid var(--grey-200);
}
}
</style>
-60
View File
@@ -147,26 +147,6 @@ describe('Filter Transformation', () => {
expect(transformed).toBe('start_date > now')
})
it('should correctly resolve label when the label is called label', () => {
const transformed = transformFilterStringForApi(
'labels = label',
(title: string) => 1,
nullTitleToIdResolver,
)
expect(transformed).toBe('labels = 1')
})
it('should correctly resolve project when the project is called project', () => {
const transformed = transformFilterStringForApi(
'project = project',
nullTitleToIdResolver,
(title: string) => 1,
)
expect(transformed).toBe('project = 1')
})
})
describe('To API', () => {
@@ -219,26 +199,6 @@ describe('Filter Transformation', () => {
expect(transformed).toBe('labels in lorem, ipsum')
})
it('should not touch the label value when it is undefined', () => {
const transformed = transformFilterStringFromApi(
'labels = one',
(id: number) => undefined,
nullIdToTitleResolver,
)
expect(transformed).toBe('labels = one')
})
it('should not touch the label value when it is null', () => {
const transformed = transformFilterStringFromApi(
'labels = one',
(id: number) => null,
nullIdToTitleResolver,
)
expect(transformed).toBe('labels = one')
})
it('should correctly resolve projects', () => {
const transformed = transformFilterStringFromApi(
'project = 1',
@@ -268,26 +228,6 @@ describe('Filter Transformation', () => {
expect(transformed).toBe('project in lorem, ipsum')
})
it('should not touch the project value when it is undefined', () => {
const transformed = transformFilterStringFromApi(
'project = one',
nullIdToTitleResolver,
(id: number) => undefined,
)
expect(transformed).toBe('project = one')
})
it('should not touch the project value when it is null', () => {
const transformed = transformFilterStringFromApi(
'project = one',
nullIdToTitleResolver,
(id: number) => null,
)
expect(transformed).toBe('project = one')
})
it('should transform the same attribute multiple times', () => {
const transformed = transformFilterStringFromApi(
+84 -63
View File
@@ -77,34 +77,49 @@ export function transformFilterStringForApi(
filter = filter.replace(new RegExp(f, 'ig'), f)
})
// Transform labels and projects to ids
function transformFieldToIds(
fields: string[],
resolver: (title: string) => number | null,
filter: string,
): string {
fields.forEach(field => {
const pattern = getFilterFieldRegexPattern(field)
let match: RegExpExecArray | null
while ((match = pattern.exec(filter)) !== null) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [matched, prefix, operator, space, keyword] = match
if (!keyword) {
continue
}
// Transform labels to ids
LABEL_FIELDS.forEach(field => {
const pattern = getFilterFieldRegexPattern(field)
let match: RegExpExecArray | null
while ((match = pattern.exec(filter)) !== null) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [matched, prefix, operator, space, keyword] = match
if (keyword) {
let keywords = [keyword.trim()]
if (operator === 'in' || operator === '?=') {
keywords = keyword.trim().split(',').map(k => k.trim())
}
keywords.forEach(k => {
const labelId = labelResolver(k)
if (labelId !== null) {
filter = filter.replace(k, String(labelId))
}
})
}
}
})
// Transform projects to ids
PROJECT_FIELDS.forEach(field => {
const pattern = getFilterFieldRegexPattern(field)
let match: RegExpExecArray | null
while ((match = pattern.exec(filter)) !== null) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [matched, prefix, operator, space, keyword] = match
if (keyword) {
let keywords = [keyword.trim()]
if (operator === 'in' || operator === '?=') {
keywords = keyword.trim().split(',').map(k => k.trim())
}
let replaced = keyword
keywords.forEach(k => {
const id = resolver(k)
if (id !== null) {
replaced = replaced.replace(k, String(id))
const projectId = projectResolver(k)
if (projectId !== null) {
replaced = replaced.replace(k, String(projectId))
}
})
@@ -113,15 +128,8 @@ export function transformFilterStringForApi(
replaced +
filter.substring(actualKeywordStart + keyword.length)
}
})
return filter
}
// Transform labels to ids
filter = transformFieldToIds(LABEL_FIELDS, labelResolver, filter)
// Transform projects to ids
filter = transformFieldToIds(PROJECT_FIELDS, projectResolver, filter)
}
})
// Transform all attributes to snake case
AVAILABLE_FILTER_FIELDS.forEach(f => {
@@ -133,8 +141,8 @@ export function transformFilterStringForApi(
export function transformFilterStringFromApi(
filter: string,
labelResolver: (id: number) => string | null | undefined,
projectResolver: (id: number) => string | null | undefined,
labelResolver: (id: number) => string | null,
projectResolver: (id: number) => string | null,
): string {
if (filter.trim() === '') {
@@ -146,40 +154,53 @@ export function transformFilterStringFromApi(
filter = filter.replaceAll(snakeCase(f), f)
})
// Function to transform fields to their titles
function transformFieldsToTitles(
fields: string[],
resolver: (id: number) => string | null | undefined,
) {
fields.forEach(field => {
const pattern = getFilterFieldRegexPattern(field)
let match: RegExpExecArray | null
while ((match = pattern.exec(filter)) !== null) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [matched, prefix, operator, space, keyword] = match
if (keyword) {
let keywords = [keyword.trim()]
if (operator === 'in' || operator === '?=') {
keywords = keyword.trim().split(',').map(k => k.trim())
}
keywords.forEach(k => {
const title = resolver(parseInt(k))
if (title) {
filter = filter.replace(k, title)
}
})
}
}
})
}
// Transform labels to their titles
transformFieldsToTitles(LABEL_FIELDS, labelResolver)
LABEL_FIELDS.forEach(field => {
const pattern = getFilterFieldRegexPattern(field)
// Transform projects to their titles
transformFieldsToTitles(PROJECT_FIELDS, projectResolver)
let match: RegExpExecArray | null
while ((match = pattern.exec(filter)) !== null) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [matched, prefix, operator, space, keyword] = match
if (keyword) {
let keywords = [keyword.trim()]
if (operator === 'in' || operator === '?=') {
keywords = keyword.trim().split(',').map(k => k.trim())
}
keywords.forEach(k => {
const labelTitle = labelResolver(parseInt(k))
if (labelTitle !== null) {
filter = filter.replace(k, labelTitle)
}
})
}
}
})
// Transform projects to ids
PROJECT_FIELDS.forEach(field => {
const pattern = getFilterFieldRegexPattern(field)
let match: RegExpExecArray | null
while ((match = pattern.exec(filter)) !== null) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [matched, prefix, operator, space, keyword] = match
if (keyword) {
let keywords = [keyword.trim()]
if (operator === 'in' || operator === '?=') {
keywords = keyword.trim().split(',').map(k => k.trim())
}
keywords.forEach(k => {
const project = projectResolver(parseInt(k))
if (project !== null) {
filter = filter.replace(k, project)
}
})
}
}
})
return filter
}
-15
View File
@@ -1,15 +0,0 @@
export function validatePassword(password: string, validateMinLength: boolean = true): string | true {
if (password === '') {
return 'user.auth.passwordRequired'
}
if (validateMinLength && password.length < 8) {
return 'user.auth.passwordNotMin'
}
if (validateMinLength && password.length > 72) {
return 'user.auth.passwordNotMax'
}
return true
}
-1
View File
@@ -25,7 +25,6 @@ export const SUPPORTED_LOCALES = {
'pt-BR': 'Português Brasileiro',
'hr-HR': 'Hrvatski',
'uk-UA': 'Українська',
'lt-LT': 'Lietuvių Kalba',
// IMPORTANT: Also add new languages to useDayjsLanguageSync
} as const
+1 -1
View File
@@ -61,7 +61,7 @@
"usernameMustNotLookLikeUrl": "The username must not look like a URL.",
"passwordRequired": "الرجاء إدخال كلمة المرور.",
"passwordNotMin": "Password must have at least 8 characters.",
"passwordNotMax": "Password must have at most 72 characters.",
"passwordNotMax": "Password must have at most 250 characters.",
"showPassword": "إظهار كلمة المرور",
"hidePassword": "إخفاء كلمة المرور",
"noAccountYet": "ليس لديك حساب بعد؟",
+1 -1
View File
@@ -61,7 +61,7 @@
"usernameMustNotLookLikeUrl": "The username must not look like a URL.",
"passwordRequired": "Please provide a password.",
"passwordNotMin": "Password must have at least 8 characters.",
"passwordNotMax": "Password must have at most 72 characters.",
"passwordNotMax": "Password must have at most 250 characters.",
"showPassword": "Show the password",
"hidePassword": "Hide the password",
"noAccountYet": "Don't have an account yet?",
+1 -1
View File
@@ -61,7 +61,7 @@
"usernameMustNotLookLikeUrl": "Uživatelské jméno nesmí vypadat jako adresa URL.",
"passwordRequired": "Zadejte prosím heslo.",
"passwordNotMin": "Heslo musí mít nejméně 8 znaků.",
"passwordNotMax": "Password must have at most 72 characters.",
"passwordNotMax": "Heslo může mít maximálně 250 znaků.",
"showPassword": "Ukázat heslo",
"hidePassword": "Skrýt heslo",
"noAccountYet": "Ještě nemáte účet?",
+1 -1
View File
@@ -61,7 +61,7 @@
"usernameMustNotLookLikeUrl": "The username must not look like a URL.",
"passwordRequired": "Angiv venligst en adgangskode.",
"passwordNotMin": "Password must have at least 8 characters.",
"passwordNotMax": "Password must have at most 72 characters.",
"passwordNotMax": "Password must have at most 250 characters.",
"showPassword": "Vis adgangskoden",
"hidePassword": "Skjul adgangskoden",
"noAccountYet": "Har du ikke en konto endnu?",
+1 -1
View File
@@ -61,7 +61,7 @@
"usernameMustNotLookLikeUrl": "Der Anmeldename darf nicht wie eine URL aussehen.",
"passwordRequired": "Bitte gib ein Passwort ein.",
"passwordNotMin": "Das Passwort muss aus mindestens 8 Zeichen bestehen.",
"passwordNotMax": "Das Passwort darf höchstens 72 Zeichen lang sein.",
"passwordNotMax": "Das Passwort darf höchstens 250 Zeichen lang sein.",
"showPassword": "Passwort anzeigen",
"hidePassword": "Passwort verbergen",
"noAccountYet": "Noch kein Account?",
+1 -1
View File
@@ -61,7 +61,7 @@
"usernameMustNotLookLikeUrl": "Der Anmeldename darf nicht wie eine URL aussehen.",
"passwordRequired": "Bitte gib ein Passwort ein.",
"passwordNotMin": "Das Passwort muss aus mindestens 8 Zeichen bestehen.",
"passwordNotMax": "Das Passwort darf höchstens 72 Zeichen lang sein.",
"passwordNotMax": "Das Passwort darf höchstens 250 Zeichen lang sein.",
"showPassword": "Passwort anzeigen",
"hidePassword": "Passwort verbergen",
"noAccountYet": "Noch kein Account?",
+2 -3
View File
@@ -61,7 +61,7 @@
"usernameMustNotLookLikeUrl": "The username must not look like a URL.",
"passwordRequired": "Please provide a password.",
"passwordNotMin": "Password must have at least 8 characters.",
"passwordNotMax": "Password must have at most 72 characters.",
"passwordNotMax": "Password must have at most 250 characters.",
"showPassword": "Show the password",
"hidePassword": "Hide the password",
"noAccountYet": "Don't have an account yet?",
@@ -848,8 +848,7 @@
"delete": "Delete this comment",
"deleteText1": "Are you sure you want to delete this comment?",
"deleteSuccess": "The comment was deleted successfully.",
"addedSuccess": "The comment was added successfully.",
"permalink": "Copy permalink to this comment"
"addedSuccess": "The comment was added successfully."
},
"deferDueDate": {
"title": "Defer due date",
+1 -1
View File
@@ -61,7 +61,7 @@
"usernameMustNotLookLikeUrl": "The username must not look like a URL.",
"passwordRequired": "Please provide a password.",
"passwordNotMin": "Password must have at least 8 characters.",
"passwordNotMax": "Password must have at most 72 characters.",
"passwordNotMax": "Password must have at most 250 characters.",
"showPassword": "Show the password",
"hidePassword": "Hide the password",
"noAccountYet": "Don't have an account yet?",
+1 -1
View File
@@ -61,7 +61,7 @@
"usernameMustNotLookLikeUrl": "The username must not look like a URL.",
"passwordRequired": "Por favor, proporciona una contraseña.",
"passwordNotMin": "Password must have at least 8 characters.",
"passwordNotMax": "Password must have at most 72 characters.",
"passwordNotMax": "Password must have at most 250 characters.",
"showPassword": "Mostrar la contraseña",
"hidePassword": "Ocultar la contraseña",
"noAccountYet": "¿Aún no tienes una cuenta?",
+1 -1
View File
@@ -61,7 +61,7 @@
"usernameMustNotLookLikeUrl": "The username must not look like a URL.",
"passwordRequired": "Veuillez fournir un mot de passe.",
"passwordNotMin": "Password must have at least 8 characters.",
"passwordNotMax": "Password must have at most 72 characters.",
"passwordNotMax": "Password must have at most 250 characters.",
"showPassword": "Afficher le mot de passe",
"hidePassword": "Masquer le mot de passe",
"noAccountYet": "Vous n'avez pas encore de compte ?",
+1 -1
View File
@@ -61,7 +61,7 @@
"usernameMustNotLookLikeUrl": "Korisničko ime ne smije izgledati kao URL.",
"passwordRequired": "Molimo upišite lozinku.",
"passwordNotMin": "Lozinka treba imati barem 8 znakova.",
"passwordNotMax": "Password must have at most 72 characters.",
"passwordNotMax": "Lozinka treba imati barem 250 znakova.",
"showPassword": "Prikaži lozinku",
"hidePassword": "Sakrij lozinku",
"noAccountYet": "Još nemate račun?",
+1 -1
View File
@@ -61,7 +61,7 @@
"usernameMustNotLookLikeUrl": "A felhasználónév nem nézhet ki URL-nek.",
"passwordRequired": "Kérjük, adjon meg új jelszót.",
"passwordNotMin": "A jelszónak legalább 8 karakterből kell állnia.",
"passwordNotMax": "Password must have at most 72 characters.",
"passwordNotMax": "A jelszó legfeljebb 250 karakterből állhat.",
"showPassword": "Jelszó megjelenítése",
"hidePassword": "A jelszó elrejtése",
"noAccountYet": "Még nincs fiókja?",
+1 -1
View File
@@ -61,7 +61,7 @@
"usernameMustNotLookLikeUrl": "The username must not look like a URL.",
"passwordRequired": "Inserisci una password.",
"passwordNotMin": "Password must have at least 8 characters.",
"passwordNotMax": "Password must have at most 72 characters.",
"passwordNotMax": "Password must have at most 250 characters.",
"showPassword": "Mostra la password",
"hidePassword": "Nascondi la password",
"noAccountYet": "Non hai un account?",
+1 -1
View File
@@ -61,7 +61,7 @@
"usernameMustNotLookLikeUrl": "The username must not look like a URL.",
"passwordRequired": "パスワードを入力してください。",
"passwordNotMin": "パスワードは8文字以上でなければなりません。",
"passwordNotMax": "Password must have at most 72 characters.",
"passwordNotMax": "パスワードは250文字以内でなければなりません。",
"showPassword": "パスワードの表示",
"hidePassword": "パスワードの非表示",
"noAccountYet": "まだアカウントをお持ちでないですか?",
+1 -1
View File
@@ -61,7 +61,7 @@
"usernameMustNotLookLikeUrl": "The username must not look like a URL.",
"passwordRequired": "비밀번호를 입력하세요.",
"passwordNotMin": "Password must have at least 8 characters.",
"passwordNotMax": "Password must have at most 72 characters.",
"passwordNotMax": "Password must have at most 250 characters.",
"showPassword": "비밀번호 표시",
"hidePassword": "비밀번호 숨김",
"noAccountYet": "아직 계정이 없으신가요?",
+233 -233
View File
@@ -1,9 +1,9 @@
{
"home": {
"welcomeNight": "Labanaktis {username}!",
"welcomeMorning": "Labas rytas {username}!",
"welcomeDay": "Labas {username}!",
"welcomeEvening": "Labas vakaras {username}!",
"welcomeNight": "Good Night {username}!",
"welcomeMorning": "Good Morning {username}!",
"welcomeDay": "Hi {username}!",
"welcomeEvening": "Good Evening {username}!",
"lastViewed": "Last viewed",
"addToHomeScreen": "Add this app to your home screen for faster access and improved experience.",
"goToOverview": "Go to overview",
@@ -33,9 +33,9 @@
},
"user": {
"auth": {
"username": "Vartotojo vardas",
"usernameEmail": "Vartotojo vardas arba el. pašto adresas",
"usernamePlaceholder": "pvz.: frederick",
"username": "Username",
"usernameEmail": "Username Or Email Address",
"usernamePlaceholder": "e.g. frederick",
"email": "Email address",
"emailPlaceholder": "e.g. frederic{'@'}vikunja.io",
"password": "Password",
@@ -56,12 +56,12 @@
"openIdGeneralError": "An error occurred while authenticating against the third party.",
"logout": "Logout",
"emailInvalid": "Please enter a valid email address.",
"usernameRequired": "Prašau nurodyti vartotojo vardą.",
"usernameMustNotContainSpace": "Vartotojo vardas negali turėti tarpų.",
"usernameMustNotLookLikeUrl": "Vartotojo vardas negali atrodyti kaip nuoroda.",
"usernameRequired": "Please provide a username.",
"usernameMustNotContainSpace": "The username must not contain spaces.",
"usernameMustNotLookLikeUrl": "The username must not look like a URL.",
"passwordRequired": "Please provide a password.",
"passwordNotMin": "Password must have at least 8 characters.",
"passwordNotMax": "Password must have at most 72 characters.",
"passwordNotMax": "Password must have at most 250 characters.",
"showPassword": "Show the password",
"hidePassword": "Hide the password",
"noAccountYet": "Don't have an account yet?",
@@ -83,12 +83,12 @@
"updateEmailSuccess": "Your email address was successfully updated. We've sent you a link to confirm it.",
"general": {
"title": "General Settings",
"name": "Mano vardas",
"newName": "Naujas vardas",
"name": "My Name",
"newName": "The new name",
"savedSuccess": "The settings were successfully updated.",
"emailReminders": "Send me reminders for tasks via email",
"overdueReminders": "Send me a summary of my undone overdue tasks every day",
"discoverableByName": "Leisti kitiems vartotojas mane pridėti prie komandų ar projektų radus mano vardą paieškoje",
"discoverableByName": "Allow other users to add me as a member to teams or projects when they search for my name",
"discoverableByEmail": "Allow other users to add me as a member to teams or projects when they search for my full email",
"playSoundWhenDone": "Play a sound when marking tasks as done",
"weekStart": "Week starts on",
@@ -99,7 +99,7 @@
"defaultView": "Default view",
"timezone": "Time zone",
"overdueTasksRemindersTime": "Overdue tasks reminder email time",
"filterUsedOnOverview": "Išsaugotas filtras naudojamas apžvalgos puslapyje"
"filterUsedOnOverview": "Saved filter used on the overview page"
},
"totp": {
"title": "Two Factor Authentication",
@@ -121,11 +121,11 @@
"more": "More information about CalDAV in Vikunja",
"tokens": "CalDAV Tokens",
"tokensHowTo": "You can use a CalDAV token to use instead of a password to log in the above endpoint.",
"createToken": "Sukurti prieigos raktą",
"createToken": "Create a token",
"tokenCreated": "Here is your token: {token}",
"wontSeeItAgain": "Write it down, you won't be able to see it again.",
"mustUseToken": "You need to create a CalDAV token if you want to use CalDAV with a third party client. Use the token as the password.",
"usernameIs": "Tavo vartotojo vardas yra: {0}"
"usernameIs": "Your username is: {0}"
},
"avatar": {
"title": "Avatar",
@@ -167,14 +167,14 @@
"expired": "This token has expired {ago}.",
"tokenCreatedSuccess": "Here is your new api token: {token}",
"tokenCreatedNotSeeAgain": "Store it in a secure location, you won't see it again!",
"selectAll": "Pažymėti viską",
"selectAll": "Select all",
"delete": {
"header": "Delete this token",
"text1": "Are you sure you want to delete the token \"{token}\"?",
"text2": "This will revoke access to all applications or integrations using it. You cannot undo this."
},
"attributes": {
"title": "Pavadinimas",
"title": "Title",
"titlePlaceholder": "Enter a title you will recognize later",
"expiresAt": "Expires at",
"permissions": "Permissions"
@@ -184,22 +184,22 @@
"deletion": {
"title": "Delete your Vikunja Account",
"text1": "The deletion of your account is permanent and cannot be undone. We will delete all your projects, tasks and everything associated with it.",
"text2": "Norėdama -s tęsti, įvesk savo slaptažodį. Gausi email'ą su tolimesnėmis instrukcijomis.",
"text2": "To proceed, please enter your password. You will receive an email with further instructions.",
"text3": "To proceed, please press the button below. You will receive an email with further instructions.",
"confirm": "Delete my account",
"requestSuccess": "Tavo užklausa sėkminga. Gausi email'ą su tolimesnėmis instrukcijomis.",
"passwordRequired": "Prašome įvesti savo slaptažodį.",
"requestSuccess": "The request was successful. You'll receive an email with further instructions.",
"passwordRequired": "Please enter your password.",
"confirmSuccess": "You've successfully confirmed the deletion of your account. We will delete your account in three days.",
"scheduled": "We will delete your Vikunja account at {date} ({dateSince}).",
"scheduledCancel": "Norėdama -s nutraukti paskyros ištrynimą, spausk čia.",
"scheduledCancelText": "Norėdama -s nutraukti paskyros ištrynimą, įveskite slaptažodį žemiau:",
"scheduledCancel": "To cancel the deletion of your account, click here.",
"scheduledCancelText": "To cancel the deletion of your account, please enter your password below:",
"scheduledCancelButton": "To cancel the deletion of your account, please press the button below:",
"scheduledCancelConfirm": "Atšaukti mano paskyros ištrynimą",
"scheduledCancelConfirm": "Cancel the deletion of my account",
"scheduledCancelSuccess": "We will not delete your account."
},
"export": {
"title": "Export your Vikunja data",
"description": "Tu gali prašyti gauti visus Vikunja duomenis. Į tai įeina, Projektai, Užduotys ir viskas, kas su jais susiję. Tu gali savo duomenis vėliau įkelti į bet kurią Vikunja aplinką naudojantis migracijos funkcija.",
"description": "You can request a copy of all your Vikunja data. This includes Projects, Tasks and everything associated to them. You can import this data in any Vikunja instance through the migration function.",
"descriptionPasswordRequired": "Please enter your password to proceed:",
"request": "Request a copy of my Vikunja Data",
"success": "You've successfully requested your Vikunja Data! We will send you an email once it's ready to download.",
@@ -256,7 +256,7 @@
},
"duplicate": {
"title": "Duplicate this project",
"label": "Dubliuoti",
"label": "Duplicate",
"text": "Select a parent project which should hold the duplicated project:",
"success": "The project was successfully duplicated."
},
@@ -265,9 +265,9 @@
"title": "Edit \"{project}\"",
"titlePlaceholder": "The project title goes here…",
"identifierTooltip": "The project identifier can be used to uniquely identify a task across projects. You can set it to empty to disable it.",
"identifier": "Projekto identifikatorius",
"identifierPlaceholder": "Projekto identifikatorius bus čia…",
"description": "Aprašymas",
"identifier": "Project Identifier",
"identifierPlaceholder": "The project identifier goes here…",
"description": "Description",
"descriptionPlaceholder": "Enter a description for this project, hit '/' for more options…",
"color": "Color",
"success": "The project was successfully updated."
@@ -281,23 +281,23 @@
"what": "What is a share link?",
"explanation": "Share Links allow you to easily share a project with other users who don't have an account on Vikunja.",
"create": "Create a link share",
"name": "Vardas (pasirinktinai)",
"namePlaceholder": "pvz.: Lorem Ipsum",
"nameExplanation": "Visi veiksmai atlikti po pasidalinta nuoroda matysis šiuo pavadinimu.",
"password": "Slaptažodis (pasirinktinai)",
"passwordExplanation": "Prisijungiant, vartotojas privalės įvesti savo slaptažodį.",
"noName": "Vardas nenustatytas",
"remove": "Pašalinti pasidalinimo nuorodą",
"name": "Name (optional)",
"namePlaceholder": "e.g. Lorem Ipsum",
"nameExplanation": "All actions done by this link share will show up with the name.",
"password": "Password (optional)",
"passwordExplanation": "When signing in, the user will be required to enter this password.",
"noName": "No name set",
"remove": "Remove a link share",
"removeText": "Are you sure you want to remove this link share? It will no longer be possible to access this project with this link share. This cannot be undone!",
"createSuccess": "Pasidalinimo nuoroda buvo sėkmingai sukurta.",
"createSuccess": "The link share was successfully created.",
"deleteSuccess": "The link share was successfully deleted",
"view": "Peržiūra",
"sharedBy": "Pasidalino {0}"
"view": "View",
"sharedBy": "Shared by {0}"
},
"userTeam": {
"typeUser": "vartotojas | vartotojai",
"typeTeam": "komanda | komandos",
"shared": "Pasidalinta su šiais {type}",
"typeUser": "user | users",
"typeTeam": "team | teams",
"shared": "Shared with these {type}",
"you": "You",
"notShared": "Not shared with any {type} yet.",
"removeHeader": "Remove a {type} from the {sharable}",
@@ -332,11 +332,11 @@
"title": "Gantt",
"size": "Size",
"default": "Default",
"month": "Mėnuo",
"day": "Diena",
"hour": "Valanda",
"month": "Month",
"day": "Day",
"hour": "Hour",
"range": "Date Range",
"noDates": "Ši užduotis dar neturi nurodytos datos."
"noDates": "This task has no dates set."
},
"table": {
"title": "Table",
@@ -373,113 +373,113 @@
}
},
"webhooks": {
"title": "„Webhook“'ai",
"targetUrl": "Tikslinė nuoroda",
"targetUrlInvalid": "Prašome nurodyti veikiančia nuorodą.",
"events": "Įvykiai",
"title": "Webhooks",
"targetUrl": "Target URL",
"targetUrlInvalid": "Please provide a valid URL.",
"events": "Events",
"eventsHint": "Select all events this webhook should recieve updates for (within the current project).",
"mustSelectEvents": "Turi pasirinkti bent vieną įvykį.",
"delete": "Ištrinti šį webhook'ą",
"mustSelectEvents": "You must select at least one event.",
"delete": "Delete this webhook",
"deleteText": "Are you sure you want to delete this webhook? External targets will not be notified of its events anymore.",
"deleteSuccess": "Webhook'as buvo sėkmingai ištrintas.",
"create": "Sukurti ryšį",
"secret": "Paslaptis",
"deleteSuccess": "The webhook was successfully deleted.",
"create": "Create webhook",
"secret": "Secret",
"secretHint": "If provided, all requests to the webhook target URL will be signed using HMAC.",
"secretDocs": "Check out the docs for more details about how to use secrets."
},
"views": {
"header": "Keisti vaizdo nustatymus",
"header": "Edit views",
"title": "Title",
"actions": "Actions",
"kind": "Kind",
"bucketConfigMode": "Bucket configuration mode",
"bucketConfig": "Telkinio konfigūracija",
"bucketConfig": "Bucket configuration",
"bucketConfigManual": "Manual",
"filter": "Filtras",
"create": "Sukurti atvaizdavimą",
"createSuccess": "Atvaizdavimas buvo sėkmingai sukurtas.",
"filter": "Filter",
"create": "Create view",
"createSuccess": "The view was created successfully.",
"titleRequired": "Please provide a title.",
"delete": "Ištrinti šį atvaizdavimą",
"delete": "Delete this view",
"deleteText": "Are you sure you want to remove this view? It will no longer be possible to use it to view tasks in this project. This action won't delete any tasks. This cannot be undone!",
"deleteSuccess": "The view was deleted successfully.",
"onlyAdminsCanEdit": "Tik projekto administratoriai gali keisti atvaizdavimą.",
"updateSuccess": "Atvaizdavimas buvo sėkmingai atnaujintas."
"onlyAdminsCanEdit": "Only project admins can edit views.",
"updateSuccess": "The view was updated successfully."
}
},
"filters": {
"title": "Filtrai",
"clear": "Anuliuoti filtrus",
"showResults": "Rodyti rezultatus",
"title": "Filters",
"clear": "Clear Filters",
"showResults": "Show results",
"attributes": {
"title": "Pavadinimas",
"titlePlaceholder": "Išsaugoto filtro pavadinimas rašomas čia…",
"description": "Aprašymas",
"descriptionPlaceholder": "Įvesti filtro aprašymą, spausti '/' norint gauti daugiau pasirinkčių…",
"includeNulls": "Įtraukti Užduotis, kurioms nenurodytos vertės",
"requireAll": "Reikalauti, kad visi filtrai būtų tikri, kad užduotys būtų rodomos",
"showDoneTasks": "Rodyti užbaigtas užduotis",
"sortAlphabetically": "Rūšiuoti alfabetiškai",
"enablePriority": "Įgalinti filtrą pagal prioritetą",
"enablePercentDone": "Įgalinti filtrą pagal progresą",
"dueDateRange": "Termino datos rėžis",
"startDateRange": "Pradžios datos rėžis",
"endDateRange": "Pabaigos datos rėžis",
"reminderRange": "Priminimo datos rėžis"
"title": "Title",
"titlePlaceholder": "The saved filter title goes here…",
"description": "Description",
"descriptionPlaceholder": "Add a description for this filter here, hit '/' for more options…",
"includeNulls": "Include Tasks which don't have a value set",
"requireAll": "Require all filters to be true for a task to show up",
"showDoneTasks": "Show Done Tasks",
"sortAlphabetically": "Sort Alphabetically",
"enablePriority": "Enable Filter By Priority",
"enablePercentDone": "Enable Filter By Progress",
"dueDateRange": "Due Date Range",
"startDateRange": "Start Date Range",
"endDateRange": "End Date Range",
"reminderRange": "Reminder Date Range"
},
"create": {
"title": "Naujas išsaugotas filtras",
"description": "Išsaugotas filtras yra virtualus projektas, kuris yra generuojamas iš pasirinktų filtrų, kiekvieną kartą jį paleidžiant.",
"action": "Sukurti išsaugotą filtrą",
"titleRequired": "Prašome nurodyti filtro pavadinimą."
"title": "New Saved Filter",
"description": "A saved filter is a virtual project which is computed from a set of filters each time it is accessed.",
"action": "Create saved filter",
"titleRequired": "Please provide a title for the filter."
},
"delete": {
"header": "Ištrinti šį užsaugotą filtrą",
"text": "Ar esi tikra-s, kad nori ištrinti išsaugotą filtrą?",
"success": "Filtras ištrintas sėkmingai."
"header": "Delete this saved filter",
"text": "Are you sure you want to delete this saved filter?",
"success": "The filter was deleted successfully."
},
"edit": {
"title": "Keisti šį išsaugotą filtrą",
"success": "Filtras išsaugotas sėkmingai."
"title": "Edit This Saved Filter",
"success": "The filter was saved successfully."
},
"query": {
"title": "Užklausa",
"placeholder": "Ieškokite ar filtruokita užklausą…",
"title": "Query",
"placeholder": "Type a search or filter query…",
"help": {
"intro": "Norėdami filtruoti užduotis, galite naudoti sintaksę panašią į SQL. Galimi filtravimo laukai įtraukia:",
"link": "Kaip tai veikia?",
"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.",
"fields": {
"done": "Ar užduotis atlikta ar ne",
"priority": "Užduoties prioriteto lygis (1-5)",
"percentDone": "Užduoties baigtumo lygis (0-100)",
"dueDate": "Užduoties termino data",
"startDate": "Užduoties pradžios data",
"endDate": "Užduoties pabaigos data",
"doneAt": "Data ir laikas, kada užduotis buvo atlikta",
"assignees": "Užduočiai priskirti vartotojai",
"labels": "Etiketės sietinos su užduotimi",
"done": "Whether the task is completed or not",
"priority": "The priority level of the task (1-5)",
"percentDone": "The percentage of completion for the task (0-100)",
"dueDate": "The due date of the task",
"startDate": "The start date of the task",
"endDate": "The end date of the task",
"doneAt": "The date and time when the task was completed",
"assignees": "The assignees of the task",
"labels": "The labels associated with the task",
"project": "The project the task belongs to (only available for saved filters, not on a project level)"
},
"operators": {
"intro": "The available operators for filtering include:",
"notEqual": "Ne lygu",
"equal": "Lygu",
"greaterThan": "Daugiau už",
"greaterThanOrEqual": "Daugiau už ar lygu",
"lessThan": "Mažiau nei",
"lessThanOrEqual": "Mažiau negu ar lygu",
"like": "Atitinka šabloną (nadojama wildcard %)",
"notEqual": "Not equal to",
"equal": "Equal to",
"greaterThan": "Greater than",
"greaterThanOrEqual": "Greater than or equal to",
"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"
},
"logicalOperators": {
"intro": "To combine multiple conditions, you can use the following logical operators:",
"and": "AND operator, matches if all conditions are true",
"or": "OR operator, matches if any of the conditions are true",
"parentheses": "Skliausteliai sąlygų grupavimui"
"parentheses": "Parentheses for grouping conditions"
},
"examples": {
"intro": "Čia pateikiami keli filtravimo užklausos pavyzdžiai:",
"priorityEqual": "Atitinka užduotis su 4 lygio prioritetu",
"intro": "Here are some examples of filter queries:",
"priorityEqual": "Matches tasks with priority level 4",
"dueDatePast": "Matches tasks with a due date in the past",
"undoneHighPriority": "Matches undone tasks with priority level 3 or higher",
"assigneesIn": "Matches tasks assigned to either \"user1\" or \"user2\"",
@@ -490,69 +490,69 @@
},
"migrate": {
"title": "Import from other services",
"titleService": "Importuoti duomenis iš {name} į Vikunja",
"titleService": "Import your data from {name} into Vikunja",
"import": "Import your data into Vikunja",
"description": "Paspauskite ant trečiosios šalies logotipo norint pradėti.",
"descriptionDo": "Vikunja importuos visus sąrašus, užduotis, užrašus, priminimus ir dokumentus prie kurių tu turi prieigą.",
"authorize": "Autorizuoti Vikunja prieigą prie tavo {name} paskyros, spauskite mygtuką žemiau.",
"getStarted": "Pradėti",
"inProgress": "Importavimas progrese…",
"alreadyMigrated1": "Atrodo, kad jau importavai savo info is {name} {date}.",
"description": "Click on the logo of one of the third-party services below to get started.",
"descriptionDo": "Vikunja will import all lists, tasks, notes, reminders and files you have access to.",
"authorize": "To authorize Vikunja to access your {name} Account, click the button below.",
"getStarted": "Get Started",
"inProgress": "Importing in progress…",
"alreadyMigrated1": "It looks like you've already imported your stuff from {name} at {date}.",
"alreadyMigrated2": "Importing again is possible, but might create duplicates. Are you sure?",
"confirm": "I am sure, please start migrating now!",
"importUpload": "Importuoti duomenis iš {name} į Vikunja, spausk mygtuką žemiau, norėdamas pasirinkti failą.",
"upload": "Įkelti failą",
"importUpload": "To import data from {name} into Vikunja, click the button below to select a file.",
"upload": "Upload file",
"migrationStartedWillReciveEmail": "Vikunja will now import your lists/projects, tasks, notes, reminders and files from {service}. As this will take a while, we will send you an email once done. You can close this window now.",
"migrationInProgress": "A migration is currently in progress. Please wait until it is done."
},
"label": {
"title": "Etiketės",
"manage": "Tvarkyti etiketes",
"description": "Paspausk ant etiketės norėdamas ją redaguoti. Tu gali keisti etiketes, kurias pats sukūrei, tu gali naudoti visas etiketes, kurios yra susietos su užduotimis, projektų, prie kurių tu turi prieigą.",
"newCTA": "Šiuo metu nėra jokių etikečių.",
"search": "Rašykite čia norėdami ieškoti etikečių…",
"title": "Labels",
"manage": "Manage labels",
"description": "Click on a label to edit it. You can edit all labels you created, you can use all labels which are associated with a task to whose project you have access.",
"newCTA": "You currently do not have any labels.",
"search": "Type to search for a label…",
"create": {
"header": "Nauja etiketė",
"title": "Sukurti etiketę",
"titleRequired": "Prašau nurodyti pavadinimą.",
"success": "Etiketė buvo sėkmingai sukurta."
"header": "New label",
"title": "Create a label",
"titleRequired": "Please specify a title.",
"success": "The label was successfully created."
},
"edit": {
"header": "Redaguoti etiketę",
"forbidden": "Tau neleidžiama redaguoti šios etiketės, kadangi ji tau nepriklauso.",
"success": "Etiketė buvo sėkmingai atnaujinta."
"header": "Edit Label",
"forbidden": "You are not allowed to edit this label because you don't own it.",
"success": "The label was successfully updated."
},
"deleteSuccess": "Etiketė buvo sėkmingai ištrinta.",
"deleteSuccess": "The label was successfully deleted.",
"attributes": {
"title": "Pavadinimas",
"titlePlaceholder": "Etiketės pavadinimas rašomas čia…",
"description": "Aprašymas",
"descriptionPlaceholder": "Etiketės aprašas",
"color": "Spalva"
"title": "Title",
"titlePlaceholder": "The label title goes here…",
"description": "Description",
"descriptionPlaceholder": "Label description",
"color": "Color"
}
},
"sharing": {
"authenticating": "Authenticating…",
"passwordRequired": "Šis pasidalintas projektas reikalauja slaptažodžio. Įveskite jį žemiau:",
"passwordRequired": "This shared project requires a password. Please enter it below:",
"error": "An error occurred.",
"invalidPassword": "Slaptažodis neteisingas."
"invalidPassword": "The password is invalid."
},
"navigation": {
"overview": "Apžvalga",
"overview": "Overview",
"upcoming": "Upcoming",
"settings": "Nustatymai",
"settings": "Settings",
"imprint": "Imprint",
"privacy": "Privacy Policy"
},
"misc": {
"loading": "Kraunama…",
"save": "Išsaugoti",
"delete": "Ištrinti",
"confirm": "Patvirtinti",
"cancel": "Atšaukti",
"refresh": "Atnaujinti",
"disable": "Išjungti",
"copy": "Kopijuoti į iškarpinę",
"loading": "Loading…",
"save": "Save",
"delete": "Delete",
"confirm": "Confirm",
"cancel": "Cancel",
"refresh": "Refresh",
"disable": "Disable",
"copy": "Copy to clipboard",
"copyError": "Copy to clipboard failed",
"search": "Search",
"searchPlaceholder": "Type to search…",
@@ -565,33 +565,33 @@
"saving": "Saving…",
"saved": "Saved!",
"default": "Default",
"close": "Uždaryti",
"download": "Atsisiųsti",
"showMenu": "Rodyti meniu",
"hideMenu": "Paslėpti meniu",
"forExample": "Pavyzdžiui:",
"welcomeBack": "Sveiki sugrįžę!",
"custom": "Pasirinktinai",
"close": "Close",
"download": "Download",
"showMenu": "Show the menu",
"hideMenu": "Hide the menu",
"forExample": "For example:",
"welcomeBack": "Welcome Back!",
"custom": "Custom",
"id": "ID",
"created": "Sukurta",
"createdBy": "Sukurta {0}",
"actions": "Veiksmai",
"cannotBeUndone": "Atšaukti negalėsite!"
"created": "Created at",
"createdBy": "Created by {0}",
"actions": "Actions",
"cannotBeUndone": "This cannot be undone!"
},
"input": {
"resetColor": "Atstatyti spalvas",
"resetColor": "Reset Color",
"datepicker": {
"today": "Šiandien",
"tomorrow": "Rytoj",
"nextMonday": "Kitą pirmadienį",
"thisWeekend": "Šį savaitgalį",
"laterThisWeek": "Vėliau šią savaitę",
"nextWeek": "Kitą savaitę",
"today": "Today",
"tomorrow": "Tomorrow",
"nextMonday": "Next Monday",
"thisWeekend": "This Weekend",
"laterThisWeek": "Later This Week",
"nextWeek": "Next Week",
"chooseDate": "Choose a date"
},
"editor": {
"edit": "Edit",
"done": "Atlikta",
"done": "Done",
"heading1": "Heading 1",
"heading1Tooltip": "Big section heading.",
"heading2": "Heading 2",
@@ -772,9 +772,9 @@
},
"actions": {
"assign": "Assign to User",
"label": "Pridėti etiketes",
"priority": "Nustatyti prioritetą",
"dueDate": "Nustatyto termino datą",
"label": "Add Labels",
"priority": "Set Priority",
"dueDate": "Set Due Date",
"startDate": "Set Start Date",
"endDate": "Set End Date",
"reminders": "Set Reminders",
@@ -783,9 +783,9 @@
"attachments": "Add Attachments",
"relatedTasks": "Add Relation",
"moveProject": "Move",
"color": "Nustatyto spalvą",
"delete": "Ištrinti",
"favorite": "Pridėti prie mėgstamiausių",
"color": "Set Color",
"delete": "Delete",
"favorite": "Add to Favorites",
"unfavorite": "Remove from Favorites"
}
},
@@ -798,7 +798,7 @@
"done": "Done",
"dueDate": "Due Date",
"endDate": "End Date",
"labels": "Etiketės",
"labels": "Labels",
"percentDone": "Progress",
"priority": "Priority",
"relatedTasks": "Related Tasks",
@@ -820,7 +820,7 @@
"subscribeSuccessProject": "You are now subscribed to this project",
"unsubscribeSuccessProject": "You are now unsubscribed to this project",
"subscribeSuccessTask": "You are now subscribed to this task",
"unsubscribeSuccessTask": "Užsiprenumeravai užduoties naujienas"
"unsubscribeSuccessTask": "You are now unsubscribed to this task"
},
"attachment": {
"title": "Attachments",
@@ -830,7 +830,7 @@
"drop": "Drop files here to upload",
"delete": "Delete attachment",
"deleteTooltip": "Delete this attachment",
"deleteText1": "Ar esi tikra -s, kad nori ištrinti šį prisegtuką {filename}?",
"deleteText1": "Are you sure you want to delete the attachment {filename}?",
"copyUrl": "Copy URL",
"copyUrlTooltip": "Copy the url of this attachment for usage in text",
"setAsCover": "Make cover",
@@ -858,8 +858,8 @@
"1week": "1 week"
},
"description": {
"placeholder": "Įvesti aprašą, spausti '/' norint gauti daugiau pasirinkčių…",
"empty": "Aprašo kol kas nėra."
"placeholder": "Enter a description, hit '/' for more options…",
"empty": "No description available yet."
},
"assignee": {
"placeholder": "Type to assign a user…",
@@ -868,16 +868,16 @@
"unassignSuccess": "The user has been unassigned successfully."
},
"label": {
"placeholder": "Rašykite noredami pridėti etiketę…",
"createPlaceholder": "Pridėti tai, kaip naują etiketę",
"addSuccess": "Etiketė buvo sėkmingai pridėta.",
"createSuccess": "Etiketė buvo sėkmingai sukurta.",
"removeSuccess": "Etiketė buvo sėkmingai anuliuota.",
"addCreateSuccess": "Etiketė buvo sėkmingai sukurta ir pridėta.",
"placeholder": "Type to add a label…",
"createPlaceholder": "Add this as new label",
"addSuccess": "The label has been added successfully.",
"createSuccess": "The label has been created successfully.",
"removeSuccess": "The label has been removed successfully.",
"addCreateSuccess": "The label has been created and added successfully.",
"delete": {
"header": "Ištrinti šią etiketę",
"text1": "Ar esi tikra(-s), kad nori ištrinti šią etiketę?",
"text2": "Šis veiksmas ištins tai iš visų užduočių ir nebus galima atkurti."
"header": "Delete this label",
"text1": "Are you sure you want to delete this label?",
"text2": "This will remove it from all tasks and cannot be restored."
}
},
"priority": {
@@ -945,16 +945,16 @@
"title": "Quick Add Magic",
"intro": "When creating a task, you can use special keywords to directly add attributes to the newly created task. This allows to add commonly used attributes to tasks much faster.",
"multiple": "You can use this multiple times.",
"label1": "Norėdama -s pridėti etiketę, paprasčiausiai naudoti etiketės prefikso pavadinimą su {prefix}.",
"label2": "Vikunja pirmiausiai patikrins ar etiketė egzistuoja, ir sukurs, jei neegzistuoja.",
"label3": "Norint naudoti tarpus, paprasčiausiai pridėkite \" ar ' prieš etiketės pavadinimą.",
"label4": "Pavyzdžiui: {prefix} \"Etiketė su tarpais\".",
"label1": "To add a label, simply prefix the name of the label with {prefix}.",
"label2": "Vikunja will first check if the label already exist and create it if not.",
"label3": "To use spaces, simply add a \" or ' around the label name.",
"label4": "For example: {prefix}\"Label with spaces\".",
"priority1": "To set a task's priority, add a number 1-5, prefixed with a {prefix}.",
"priority2": "The higher the number, the higher the priority.",
"assignees": "Norint tiesiogiai priskirti užduotį vartotojui, pridėkite vartotojo vardą kaip prefiksą su {prefix} prie užduoties.",
"project1": "Nustatyti užduoties projektą, įveskite prefiksą su {prefix}.",
"assignees": "To directly assign the task to a user, add their username prefixed with {prefix} to the task.",
"project1": "To set a project for the task to appear in, enter its name prefixed with {prefix}.",
"project2": "This will return an error if the project does not exist.",
"project3": "Norint naudoti tarpus, paprasčiausiai pridėkite \" ar ' apie projekto pavadinimą.",
"project3": "To use spaces, simply add a \" or ' around the project name.",
"project4": "For example: {prefix}\"Project with spaces\".",
"dateAndTime": "Date and time",
"date": "Any date will be used as the due date of the new task. You can use dates in any of these formats:",
@@ -963,7 +963,7 @@
"dateNth": "will use the {day}th of the current month",
"dateTime": "Combine any of the date formats with \"{time}\" (or {timePM}) to set a time.",
"repeats": "Repeating tasks",
"repeatsDescription": "Norėdama -s nustatyti, kad užduotis kartotųsi tam tikru intervalu, paprasčiausiai pridėk '{suffix}' prie užduoties teksto. Kiekis turi būti išreikštas skaičiumi, tačiau gali būti ir praleistas (žr. pavyzdžius)."
"repeatsDescription": "To set a task as repeating in an interval, simply add '{suffix}' to the task text. The amount needs to be a number and can be omitted to use just the type (see examples)."
}
},
"team": {
@@ -1005,15 +1005,15 @@
}
},
"attributes": {
"name": "Komandos Pavadinimas",
"namePlaceholder": "Komandos pavadinimas rašomas čia…",
"nameRequired": "Prašome nurodyti vardą.",
"description": "Aprašymas",
"descriptionPlaceholder": "Aprašykite komandą čia, spauskite '/' dėl daugiau opcijų…",
"name": "Team Name",
"namePlaceholder": "The team's name goes here…",
"nameRequired": "Please specify a name.",
"description": "Description",
"descriptionPlaceholder": "Describe the team here, hit '/' for more options…",
"admin": "Admin",
"member": "Member",
"isPublic": "Public Team",
"isPublicDescription": "Padaryk komandą viešai randamą. Kai įgalinta, bet kas gali pasidalinti projektu su komanda, net nebūdama -s tiesioginiu komandos nariu."
"isPublicDescription": "Make the team publicly discoverable. When enabled, anyone can share projects with this team even when not being a direct member."
}
},
"keyboardShortcuts": {
@@ -1029,14 +1029,14 @@
"title": "Task Page",
"done": "Mark task done / undone",
"assign": "Assign this task to a user",
"labels": "Pridėti etiketes šiai užduočiai",
"labels": "Add labels to this task",
"dueDate": "Change the due date of this task",
"attachment": "Add an attachment to this task",
"related": "Modify related tasks of this task",
"color": "Change the color of this task",
"move": "Move this task to another project",
"reminder": "Manage reminders of this task",
"description": "Praskleisti redagavimą užduoties apraše",
"description": "Toggle editing of the task description",
"delete": "Delete this task",
"priority": "Change the priority of this task",
"favorite": "Mark this task as favorite / unfavorite",
@@ -1053,7 +1053,7 @@
"title": "Navigation",
"overview": "Navigate to overview",
"upcoming": "Navigate to upcoming tasks",
"labels": "Eiti į etiketes",
"labels": "Navigate to labels",
"teams": "Navigate to teams",
"projects": "Navigate to projects"
}
@@ -1100,14 +1100,14 @@
"quickActions": {
"commands": "Commands",
"placeholder": "Type a command or search…",
"hint": "Gai naudoti {project} sutrumpinti projektų paiešką. Sujungti {project} ar {label} (etiketes) paieškos užklausoje norint ieškoti užduoties su tomis etiketėmis ar tame projekte. Naudok {assignee} norėdama(-s) ieškoti komandu.",
"hint": "You can use {project} to limit the search to a project. Combine {project} or {label} (labels) with a search query to search for a task with these labels or on that project. Use {assignee} to only search for teams.",
"tasks": "Tasks",
"projects": "Projects",
"teams": "Teams",
"labels": "Etiketės",
"labels": "Labels",
"newProject": "Enter the title of the new project…",
"newTask": "Enter the title of the new task…",
"newTeam": "Įveskite naujos komandos pavadinimą…",
"newTeam": "Enter the name of the new team…",
"createTask": "Create a task in the current project ({title})",
"createProject": "Create a project",
"cmds": {
@@ -1131,15 +1131,15 @@
"error": "Error",
"success": "Success",
"0001": "You're not allowed to do that.",
"1001": "Vartotojas tokiu vardu jau egzistuoja.",
"1001": "A user with this username already exists.",
"1002": "A user with this email address already exists.",
"1004": "Nenurodytas vartotojo vardas ir slaptažodis.",
"1004": "No username and password specified.",
"1005": "The user does not exist.",
"1006": "Could not get the user id.",
"1008": "No password reset token provided.",
"1009": "Invalid password reset token.",
"1010": "Invalid email confirm token.",
"1011": "Neteisingas prisijungimo vardas arba slaptažodis.",
"1011": "Wrong username or password.",
"1012": "Email address of the user not confirmed.",
"1013": "New password is empty.",
"1014": "Old password is empty.",
@@ -1172,9 +1172,9 @@
"4015": "The task comment does not exist.",
"4016": "Invalid task field.",
"4017": "Invalid task filter comparator.",
"4018": "Neteisingas užduoties filtro jungtukas.",
"4019": "Neteisinga užduotis filtro vertė.",
"6001": "Komandos pavadinimas negali būti tuščias.",
"4018": "Invalid task filter concatenator.",
"4019": "Invalid task filter value.",
"6001": "The team name cannot be empty.",
"6002": "The team does not exist.",
"6004": "The team already has access to that project.",
"6005": "The user is already a member of that team.",
@@ -1182,35 +1182,35 @@
"6007": "The team does not have access to the project to perform that action.",
"7002": "The user already has access to that project.",
"7003": "You do not have access to that project.",
"8001": "Ši etiketė jau yra prie šios užduoties.",
"8002": "Etiketė neegzistuoja.",
"8001": "This label already exists on that task.",
"8002": "The label does not exist.",
"8003": "You do not have access to this label.",
"9001": "The right is invalid.",
"10001": "Šaltinis neegzistuoja.",
"10001": "The bucket does not exist.",
"10002": "The bucket does not belong to that project.",
"10003": "You cannot remove the last bucket on a project.",
"10004": "You cannot add the task to this bucket as it already exceeded the limit of tasks it can hold.",
"10005": "There can be only one done bucket per project.",
"11001": "Išsaugotas filtras neegzistuoja.",
"11002": "Išsaugotu filtru negalima pasidalinti.",
"11001": "The saved filter does not exist.",
"11002": "Saved filters are not available for link shares.",
"12001": "The subscription entity type is invalid.",
"12002": "You are already subscribed to the entity itself or a parent entity.",
"13001": "Šis dalinimasis nuoroda reikalauja slaptažodžio identifikavimuisi, bet jis nebuvo pateiktas.",
"13002": "Pateikta slaptažodžio dalinimosi nuoroda negalioja."
"13001": "This link share requires a password for authentication, but none was provided.",
"13002": "The provided link share password was invalid."
},
"about": {
"title": "Apie",
"version": "Versija: {version}"
"title": "About",
"version": "Version: {version}"
},
"time": {
"units": {
"seconds": "sekundė|sekundės",
"minutes": "minutė|minutės",
"hours": "valanda|valandos",
"days": "diena|dienos",
"weeks": "savaitė|savaitės",
"months": "mėnesis|mėnesiai",
"years": "metai|metai"
"seconds": "second|seconds",
"minutes": "minute|minutes",
"hours": "hour|hours",
"days": "day|days",
"weeks": "week|weeks",
"months": "month|months",
"years": "year|years"
}
}
}
+1 -1
View File
@@ -61,7 +61,7 @@
"usernameMustNotLookLikeUrl": "The username must not look like a URL.",
"passwordRequired": "Please provide a password.",
"passwordNotMin": "Password must have at least 8 characters.",
"passwordNotMax": "Password must have at most 72 characters.",
"passwordNotMax": "Password must have at most 250 characters.",
"showPassword": "Show the password",
"hidePassword": "Hide the password",
"noAccountYet": "Don't have an account yet?",
+1 -1
View File
@@ -61,7 +61,7 @@
"usernameMustNotLookLikeUrl": "The username must not look like a URL.",
"passwordRequired": "Angi et passord.",
"passwordNotMin": "Password must have at least 8 characters.",
"passwordNotMax": "Password must have at most 72 characters.",
"passwordNotMax": "Password must have at most 250 characters.",
"showPassword": "Vis passord",
"hidePassword": "Skjul passord",
"noAccountYet": "Har du ikke konto ennå?",
+1 -1
View File
@@ -61,7 +61,7 @@
"usernameMustNotLookLikeUrl": "Nazwa użytkownika nie może wyglądać jak adres URL.",
"passwordRequired": "Proszę podać hasło.",
"passwordNotMin": "Hasło musi zawierać co najmniej 8 znaków.",
"passwordNotMax": "Password must have at most 72 characters.",
"passwordNotMax": "Hasło musi zawierać co najwyżej 250 znaków.",
"showPassword": "Pokaż hasło",
"hidePassword": "Ukryj hasło",
"noAccountYet": "Nie masz jeszcze konta?",
+1 -1
View File
@@ -61,7 +61,7 @@
"usernameMustNotLookLikeUrl": "The username must not look like a URL.",
"passwordRequired": "Por favor, insira uma senha.",
"passwordNotMin": "Password must have at least 8 characters.",
"passwordNotMax": "Password must have at most 72 characters.",
"passwordNotMax": "Password must have at most 250 characters.",
"showPassword": "Exibir senha",
"hidePassword": "Ocultar senha",
"noAccountYet": "Não possui uma conta ainda?",
+1 -1
View File
@@ -61,7 +61,7 @@
"usernameMustNotLookLikeUrl": "O nome de utilizador não se deve assemelhar a um URL.",
"passwordRequired": "Por favor, fornece uma palavra-passe.",
"passwordNotMin": "A palavra-passe deve ter no mínimo 8 caracteres.",
"passwordNotMax": "Password must have at most 72 characters.",
"passwordNotMax": "A palavra-passe deve ter no máximo 250 caracteres.",
"showPassword": "Mostrar a palavra-passe",
"hidePassword": "Esconder a palavra-passe",
"noAccountYet": "Ainda não tens uma conta?",
+1 -1
View File
@@ -61,7 +61,7 @@
"usernameMustNotLookLikeUrl": "The username must not look like a URL.",
"passwordRequired": "Please provide a password.",
"passwordNotMin": "Password must have at least 8 characters.",
"passwordNotMax": "Password must have at most 72 characters.",
"passwordNotMax": "Password must have at most 250 characters.",
"showPassword": "Show the password",
"hidePassword": "Hide the password",
"noAccountYet": "Don't have an account yet?",
+1 -1
View File
@@ -61,7 +61,7 @@
"usernameMustNotLookLikeUrl": "Имя пользователя не должно быть похожим на URL.",
"passwordRequired": "Введите пароль.",
"passwordNotMin": "Пароль должен содержать не меньше 8 символов.",
"passwordNotMax": "Password must have at most 72 characters.",
"passwordNotMax": "Пароль должен содержать не больше 250 символов.",
"showPassword": "Показать пароль",
"hidePassword": "Скрыть пароль",
"noAccountYet": "Ещё нет аккаунта?",
+1 -1
View File
@@ -61,7 +61,7 @@
"usernameMustNotLookLikeUrl": "The username must not look like a URL.",
"passwordRequired": "Please provide a password.",
"passwordNotMin": "Password must have at least 8 characters.",
"passwordNotMax": "Password must have at most 72 characters.",
"passwordNotMax": "Password must have at most 250 characters.",
"showPassword": "Show the password",
"hidePassword": "Hide the password",
"noAccountYet": "Don't have an account yet?",
+1 -1
View File
@@ -61,7 +61,7 @@
"usernameMustNotLookLikeUrl": "Uporabniško ime ne sme izgledati kot URL.",
"passwordRequired": "Prosim vnesite geslo.",
"passwordNotMin": "Geslo mora imeti vsaj 8 znakov.",
"passwordNotMax": "Geslo mora imeti največ 72 znakov.",
"passwordNotMax": "Geslo mora imeti največ 250 znakov.",
"showPassword": "Prikažite geslo",
"hidePassword": "Skrijte geslo",
"noAccountYet": "Še nimate računa?",
+1 -1
View File
@@ -61,7 +61,7 @@
"usernameMustNotLookLikeUrl": "The username must not look like a URL.",
"passwordRequired": "Please provide a password.",
"passwordNotMin": "Password must have at least 8 characters.",
"passwordNotMax": "Password must have at most 72 characters.",
"passwordNotMax": "Password must have at most 250 characters.",
"showPassword": "Show the password",
"hidePassword": "Hide the password",
"noAccountYet": "Don't have an account yet?",
+11 -11
View File
@@ -60,8 +60,8 @@
"usernameMustNotContainSpace": "The username must not contain spaces.",
"usernameMustNotLookLikeUrl": "The username must not look like a URL.",
"passwordRequired": "Vänligen ange ett lösenord.",
"passwordNotMin": "Lösenord måste innehålla minst 8 tecken.",
"passwordNotMax": "Password must have at most 72 characters.",
"passwordNotMin": "Password must have at least 8 characters.",
"passwordNotMax": "Password must have at most 250 characters.",
"showPassword": "Show the password",
"hidePassword": "Hide the password",
"noAccountYet": "Har du inget konto än?",
@@ -74,7 +74,7 @@
"newPasswordTitle": "Uppdatera ditt lösenord",
"newPassword": "Nytt lösenord",
"newPasswordConfirm": "New password confirmation",
"currentPassword": "Nuvarande lösenord",
"currentPassword": "Current password",
"currentPasswordPlaceholder": "Ditt nuvarande lösenord",
"passwordsDontMatch": "The new password and its confirmation don't match.",
"passwordUpdateSuccess": "The password was successfully updated.",
@@ -83,7 +83,7 @@
"updateEmailSuccess": "Your email address was successfully updated. We've sent you a link to confirm it.",
"general": {
"title": "General Settings",
"name": "Mitt namn",
"name": "My Name",
"newName": "The new name",
"savedSuccess": "The settings were successfully updated.",
"emailReminders": "Send me reminders for tasks via email",
@@ -325,7 +325,7 @@
"add": "Lägg till",
"addPlaceholder": "Add a task…",
"empty": "This project is currently empty.",
"newTaskCta": "Skapa en uppgift.",
"newTaskCta": "Create a task.",
"editTask": "Redigera uppgift"
},
"gantt": {
@@ -396,7 +396,7 @@
"bucketConfig": "Bucket configuration",
"bucketConfigManual": "Manual",
"filter": "Filter",
"create": "Skapa vy",
"create": "Create view",
"createSuccess": "The view was created successfully.",
"titleRequired": "Please provide a title.",
"delete": "Delete this view",
@@ -575,7 +575,7 @@
"id": "ID",
"created": "Created at",
"createdBy": "Created by {0}",
"actions": "Åtgärder",
"actions": "Actions",
"cannotBeUndone": "This cannot be undone!"
},
"input": {
@@ -1111,7 +1111,7 @@
"createTask": "Create a task in the current project ({title})",
"createProject": "Skapa ett projekt",
"cmds": {
"newTask": "Ny uppgift",
"newTask": "New task",
"newProject": "Nytt projekt",
"newTeam": "Nytt team"
}
@@ -1122,10 +1122,10 @@
"altFormatShort": "j M Y"
},
"reaction": {
"reactedWith": "{user} reagerade med {value}",
"reactedWithAnd": "{users} och {lastUser} reagerade med {value}",
"reactedWith": "{user} reacted with {value}",
"reactedWithAnd": "{users} and {lastUser} reacted with {value}",
"reactedWithAndMany": "{users} and {num} more reacted reacted with {value}",
"add": "Lägg till din reaktion"
"add": "Add your reaction"
},
"error": {
"error": "Error",
+1 -1
View File
@@ -61,7 +61,7 @@
"usernameMustNotLookLikeUrl": "The username must not look like a URL.",
"passwordRequired": "Please provide a password.",
"passwordNotMin": "Password must have at least 8 characters.",
"passwordNotMax": "Password must have at most 72 characters.",
"passwordNotMax": "Password must have at most 250 characters.",
"showPassword": "Show the password",
"hidePassword": "Hide the password",
"noAccountYet": "Don't have an account yet?",
+1 -1
View File
@@ -61,7 +61,7 @@
"usernameMustNotLookLikeUrl": "Ім'я вживача не має виглядати як посилання.",
"passwordRequired": "Будь ласка, введіть пароль.",
"passwordNotMin": "Пароль має містити принаймні 8 знаків.",
"passwordNotMax": "Password must have at most 72 characters.",
"passwordNotMax": "Пароль має містити не більше 250 знаків.",
"showPassword": "Показати пароль",
"hidePassword": "Сховати пароль",
"noAccountYet": "Досі немає обліковки?",
+1 -1
View File
@@ -61,7 +61,7 @@
"usernameMustNotLookLikeUrl": "The username must not look like a URL.",
"passwordRequired": "Vui lòng cung cấp một mật khẩu.",
"passwordNotMin": "Password must have at least 8 characters.",
"passwordNotMax": "Password must have at most 72 characters.",
"passwordNotMax": "Password must have at most 250 characters.",
"showPassword": "Hiển thị mật khẩu",
"hidePassword": "Ẩn mật khẩu",
"noAccountYet": "Bạn chưa có tài khoản?",
+1 -1
View File
@@ -61,7 +61,7 @@
"usernameMustNotLookLikeUrl": "用户名不能像一个 URL。",
"passwordRequired": "请提供密码",
"passwordNotMin": "密码至少有8个字符",
"passwordNotMax": "Password must have at most 72 characters.",
"passwordNotMax": "密码不能超过250个字符",
"showPassword": "显示密码",
"hidePassword": "隐藏密码",
"noAccountYet": "还没有账号?",
+1 -1
View File
@@ -61,7 +61,7 @@
"usernameMustNotLookLikeUrl": "The username must not look like a URL.",
"passwordRequired": "Please provide a password.",
"passwordNotMin": "Password must have at least 8 characters.",
"passwordNotMax": "Password must have at most 72 characters.",
"passwordNotMax": "Password must have at most 250 characters.",
"showPassword": "Show the password",
"hidePassword": "Hide the password",
"noAccountYet": "Don't have an account yet?",
@@ -25,7 +25,6 @@ export const DAYJS_LOCALE_MAPPING = {
'pt-BR': 'pt',
'hr-HR': 'hr',
'uk-UA': 'uk',
'lt-LT': 'lt',
} as Record<SupportedLocale, ISOLanguage>
export const DAYJS_LANGUAGE_IMPORTS = {
@@ -49,7 +48,6 @@ export const DAYJS_LANGUAGE_IMPORTS = {
'sl-si': () => import('dayjs/locale/sl'),
'pt-br': () => import('dayjs/locale/pt-br'),
'uk-ua': () => import('dayjs/locale/uk'),
'lt-lt': () => import('dayjs/locale/lt'),
} as Record<SupportedLocale, () => Promise<ILocale>>
export function useDayjsLanguageSync(dayjsGlobal: typeof dayjs) {
+3 -4
View File
@@ -1,12 +1,11 @@
import type {IAbstract} from './IAbstract'
import type {IUser} from './IUser'
// FIXME: what makes this different from TaskFilterParams?
interface Filters {
sort_by: ('start_date' | 'done' | 'id' | 'position')[],
order_by: ('asc' | 'desc')[],
sortBy: ('start_date' | 'done' | 'id' | 'position')[],
orderBy: ('asc' | 'desc')[],
filter: string,
filter_include_nulls: boolean,
filterIncludeNulls: boolean,
s: string,
}
-4
View File
@@ -7,10 +7,6 @@ import type { IAttachment } from '@/modelTypes/IAttachment'
export const SUPPORTED_IMAGE_SUFFIX = ['.jpg', '.png', '.bmp', '.gif']
export function canPreview(attachment: IAttachment): boolean {
return SUPPORTED_IMAGE_SUFFIX.some((suffix) => attachment.file.name.toLowerCase().endsWith(suffix))
}
export default class AttachmentModel extends AbstractModel<IAttachment> implements IAttachment {
id = 0
taskId = 0
+3 -8
View File
@@ -1,4 +1,3 @@
import { objectToSnakeCase } from '@/helpers/case'
import AbstractModel from './abstractModel'
import UserModel from '@/models/user'
@@ -10,10 +9,10 @@ export default class SavedFilterModel extends AbstractModel<ISavedFilter> implem
title = ''
description = ''
filters: ISavedFilter['filters'] = {
sort_by: ['done', 'id'],
order_by: ['asc', 'desc'],
sortBy: ['done', 'id'],
orderBy: ['asc', 'desc'],
filter: 'done = false',
filter_include_nulls: true,
filterIncludeNulls: true,
s: '',
}
@@ -27,10 +26,6 @@ export default class SavedFilterModel extends AbstractModel<ISavedFilter> implem
this.owner = new UserModel(this.owner)
// Filters are in snake_case for the API - this makes it consistent with the way filter params are used with one-off filters.
// Should probably be camelCase everywhere, but that's a task for another day.
this.filters = objectToSnakeCase(this.filters)
this.created = new Date(this.created)
this.updated = new Date(this.updated)
}
+2 -14
View File
@@ -5,13 +5,6 @@ import type { IAttachment } from '@/modelTypes/IAttachment'
import {downloadBlob} from '@/helpers/downloadBlob'
export enum PREVIEW_SIZE {
SM = 'sm',
MD = 'md',
LG = 'lg',
XL = 'xl',
}
export default class AttachmentService extends AbstractService<IAttachment> {
constructor() {
super({
@@ -44,13 +37,8 @@ export default class AttachmentService extends AbstractService<IAttachment> {
return data
}
getBlobUrl(model: IAttachment, size?: PREVIEW_SIZE) {
let mainUrl = '/tasks/' + model.taskId + '/attachments/' + model.id
if (size !== undefined) {
mainUrl += `?preview_size=${size}`
}
return AbstractService.prototype.getBlobUrl.call(this, mainUrl)
getBlobUrl(model: IAttachment) {
return AbstractService.prototype.getBlobUrl.call(this, '/tasks/' + model.taskId + '/attachments/' + model.id)
}
async download(model: IAttachment) {
+23 -1
View File
@@ -13,6 +13,7 @@ import SavedFilterModel from '@/models/savedFilter'
import {useBaseStore} from '@/stores/base'
import {useProjectStore} from '@/stores/projects'
import {objectToSnakeCase, objectToCamelCase} from '@/helpers/case'
import {success} from '@/message'
import ProjectModel from '@/models/project'
@@ -54,6 +55,23 @@ export default class SavedFilterService extends AbstractService<ISavedFilter> {
modelFactory(data) {
return new SavedFilterModel(data)
}
processModel(model) {
// Make filters from this.filters camelCase and set them to the model property:
// That's easier than making the whole filter component configurable since that still needs to provide
// the filter values in snake_sćase for url parameters.
model.filters = objectToCamelCase(model.filters)
return model
}
beforeUpdate(model) {
return this.processModel(model)
}
beforeCreate(model) {
return this.processModel(model)
}
}
export function useSavedFilter(projectId?: MaybeRefOrGetter<IProject['id']>) {
@@ -80,7 +98,10 @@ export function useSavedFilter(projectId?: MaybeRefOrGetter<IProject['id']>) {
// We assume the projectId in the route is the pseudoproject
const savedFilterId = getSavedFilterIdFromProjectId(watchedProjectId)
filter.value = await filterService.get(new SavedFilterModel({id: savedFilterId}))
filter.value = new SavedFilterModel({id: savedFilterId})
const response = await filterService.get(filter.value)
response.filters = objectToSnakeCase(response.filters)
filter.value = response
await validateTitleField()
}, {immediate: true})
@@ -94,6 +115,7 @@ export function useSavedFilter(projectId?: MaybeRefOrGetter<IProject['id']>) {
const response = await filterService.update(filter.value)
await projectStore.loadAllProjects()
success({message: t('filters.edit.success')})
response.filters = objectToSnakeCase(response.filters)
filter.value = response
await useBaseStore().setCurrentProject(new ProjectModel({
id: getProjectId(filter.value),
-8
View File
@@ -31,17 +31,9 @@ export default class TaskCollectionService extends AbstractService<ITask> {
constructor() {
super({
getAll: '/projects/{projectId}/views/{viewId}/tasks',
// /projects/{projectId}/tasks when viewId is not provided
})
}
getReplacedRoute(path: string, pathparams: Record<string, unknown>): string {
if (!pathparams.viewId) {
return super.getReplacedRoute('/projects/{projectId}/tasks', pathparams)
}
return super.getReplacedRoute(path, pathparams)
}
modelFactory(data) {
// FIXME: There must be a better way for this…
if (typeof data.project_view_id !== 'undefined') {
+1 -1
View File
@@ -194,8 +194,8 @@ export const useKanbanStore = defineStore('kanban', () => {
...oldBucket,
count: (oldBucket?.count || 0) + 1,
tasks: [
task,
...oldBucket.tasks,
task,
],
}
buckets.value[bucketIndex] = newBucket
+2 -2
View File
@@ -44,12 +44,12 @@ export const useLabelStore = defineStore('label', () => {
// **
// * Checks if a list of labels is available in the store and filters them then query
// * Checks if a project of labels is available in the store and filters them then query
// **
const filterLabelsByQuery = computed(() => {
return (labelsToHide: ILabel[], query: string) => {
const labelIdsToHide: number[] = labelsToHide.map(({id}) => id)
return search(query)
?.filter(value => !labelIdsToHide.includes(value))
.map(id => labels.value[id])
+2 -2
View File
@@ -66,7 +66,7 @@ export const useProjectStore = defineStore('project', () => {
return search(query)
?.filter(value => value > 0)
.map(id => projects.value[id])
.filter(project => project?.isArchived === includeArchived)
.filter(project => project.isArchived === includeArchived)
|| []
}
})
@@ -76,7 +76,7 @@ export const useProjectStore = defineStore('project', () => {
return search(query)
?.filter(value => getSavedFilterIdFromProjectId(value) > 0)
.map(id => projects.value[id])
.filter(project => project?.isArchived === includeArchived)
.filter(project => project.isArchived === includeArchived)
|| []
}
})
-3
View File
@@ -414,7 +414,6 @@ export const useTaskStore = defineStore('task', () => {
bucketId,
projectId,
position,
index,
} :
Partial<ITask>,
) {
@@ -430,7 +429,6 @@ export const useTaskStore = defineStore('task', () => {
projectId,
bucketId,
position,
index,
}))
} finally {
cancel()
@@ -469,7 +467,6 @@ export const useTaskStore = defineStore('task', () => {
assignees,
bucketId: bucketId || 0,
position,
index,
})
task.repeatAfter = parsedTask.repeats
+10 -9
View File
@@ -5,16 +5,17 @@
outline: none;
}
a, button {
&:focus-visible {
box-shadow: 0 0 0 2px hsla(var(--primary-hsl), 0.5);
}
:focus {
box-shadow: 0 0 0 2px hsla(var(--primary-hsl), 0.5);
}
@supports not selector(:focus-visible) {
&:focus {
box-shadow: 0 0 0 2px hsla(var(--primary-hsl), 0.5);
}
}
:focus:not(:focus-visible) {
outline: 0;
}
:focus-visible,
:-moz-focusring {
box-shadow: 0 0 0 2px hsla(var(--primary-hsl), 0.5);
}
:root {
+1 -1
View File
@@ -6,7 +6,7 @@ import {getFullBaseUrl} from './helpers/getFullBaseUrl'
declare let self: ServiceWorkerGlobalScope
const fullBaseUrl = getFullBaseUrl()
const workboxVersion = 'v7.1.0'
const workboxVersion = 'v7.0.0'
importScripts(`${fullBaseUrl}workbox-${workboxVersion}/workbox-sw.js`)
workbox.setConfig({
-1
View File
@@ -42,7 +42,6 @@
<ProjectCardGrid
v-cy="'projectCardGrid'"
:projects="projectHistory"
:show-even-number-of-projects="true"
/>
</div>
<ShowTasks
-1
View File
@@ -63,7 +63,6 @@
:disabled="filterService.loading"
class="has-no-shadow has-no-border"
:has-footer="false"
:change-immediately="true"
/>
</div>
</div>
@@ -99,7 +99,7 @@ async function saveView(view: IProjectView) {
if (view?.viewKind !== 'kanban') {
view.bucketConfigurationMode = 'none'
}
const result = await projectViewService.update(view)
const result = await ProjectViewService.update(view)
projectStore.setProjectView(result)
viewToEdit.value = null
success({message: t('project.views.updateSuccess')})
+3 -6
View File
@@ -86,7 +86,6 @@ import {useAuthStore} from '@/stores/auth'
import {useTaskStore} from '@/stores/tasks'
import {useProjectStore} from '@/stores/projects'
import type {TaskFilterParams} from '@/services/taskCollection'
import TaskCollectionService from '@/services/taskCollection'
const props = withDefaults(defineProps<{
dateFrom?: Date | string,
@@ -106,7 +105,6 @@ const {t} = useI18n({useScope: 'global'})
const tasks = ref<ITask[]>([])
const showNothingToDo = ref<boolean>(false)
const taskCollectionService = ref(new TaskCollectionService())
const projectStore = useProjectStore()
@@ -133,7 +131,7 @@ const pageTitle = computed(() => {
})
const hasTasks = computed(() => tasks.value && tasks.value.length > 0)
const userAuthenticated = computed(() => authStore.authenticated)
const loading = computed(() => taskStore.isLoading || taskCollectionService.value.loading)
const loading = computed(() => taskStore.isLoading)
interface dateStrings {
dateFrom: string,
@@ -201,9 +199,8 @@ async function loadPendingTasks(from: Date|string, to: Date|string) {
}
}
const filterId = authStore.settings.frontendSettings.filterIdUsedOnOverview
if (showAll.value && filterId && typeof projectStore.projects[filterId] !== 'undefined') {
tasks.value = await taskCollectionService.value.getAll({projectId: filterId}, params)
if (showAll.value && authStore.settings.frontendSettings.filterIdUsedOnOverview && typeof projectStore.projects[authStore.settings.frontendSettings.filterIdUsedOnOverview] !== 'undefined') {
tasks.value = await taskStore.loadTasks(params, authStore.settings.frontendSettings.filterIdUsedOnOverview)
return
}
+2 -2
View File
@@ -16,7 +16,6 @@
:task="task"
:can-write="canWrite"
@update:task="Object.assign(task, $event)"
@close="$emit('close')"
/>
<h6
v-if="project?.id"
@@ -971,8 +970,9 @@ function setRelatedTasksActive() {
color: var(--text);
background-color: var(--site-background) !important;
@media screen and (max-width: calc(#{$desktop} + 1px)) {
@media screen and (max-width: 800px) {
border-radius: 0;
padding-top: 2rem;
}
}
+1 -2
View File
@@ -127,7 +127,6 @@ import Password from '@/components/input/Password.vue'
import {useAuthStore} from '@/stores/auth'
import {useConfigStore} from '@/stores/config'
import {validatePassword} from '@/helpers/validatePasswort'
const {t} = useI18n()
const authStore = useAuthStore()
@@ -184,7 +183,7 @@ const validateUsername = useDebounceFn(() => {
const everythingValid = computed(() => {
return credentials.username !== '' &&
credentials.email !== '' &&
validatePassword(credentials.password) === true &&
credentials.password !== '' &&
emailValid.value &&
usernameValid.value
})
+2 -2
View File
@@ -6,8 +6,8 @@ module.exports = {
},
prefix: 'tw-',
content: [
'./index.html',
'./src/**/*.{vue,js,ts}',
"./index.html",
"./src/**/*.{vue,js,ts}",
],
theme: {
extend: {},
+29 -30
View File
@@ -1,24 +1,25 @@
// Vikunja is a to-do list application to facilitate your life.
// Copyright 2018-present Vikunja and contributors. All rights reserved.
// Copyright 2018 Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public Licensee as published by
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public Licensee for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public Licensee
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
module code.vikunja.io/api
require (
dario.cat/mergo v1.0.1
github.com/ThreeDotsLabs/watermill v1.3.7
code.vikunja.io/web v0.0.0-20210706160506-d85def955bd3
dario.cat/mergo v1.0.0
github.com/ThreeDotsLabs/watermill v1.3.5
github.com/adlio/trello v1.12.0
github.com/arran4/golang-ical v0.3.1
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2
@@ -29,9 +30,9 @@ require (
github.com/d4l3k/messagediff v1.2.1
github.com/disintegration/imaging v1.6.2
github.com/dustinkirkland/golang-petname v0.0.0-20240422154211-76c06c4bde6b
github.com/gabriel-vasile/mimetype v1.4.5
github.com/gabriel-vasile/mimetype v1.4.4
github.com/ganigeorgiev/fexpr v0.4.1
github.com/getsentry/sentry-go v0.29.0
github.com/getsentry/sentry-go v0.28.1
github.com/go-sql-driver/mysql v1.8.1
github.com/go-testfixtures/testfixtures/v3 v3.11.0
github.com/gocarina/gocsv v0.0.0-20231116093920-b87c2d0e983a
@@ -49,13 +50,13 @@ require (
github.com/labstack/gommon v0.4.2
github.com/lib/pq v1.10.9
github.com/magefile/mage v1.15.0
github.com/mattn/go-sqlite3 v1.14.23
github.com/mattn/go-sqlite3 v1.14.22
github.com/microcosm-cc/bluemonday v1.0.27
github.com/olekukonko/tablewriter v0.0.5
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
github.com/pquerna/otp v1.4.0
github.com/prometheus/client_golang v1.20.4
github.com/redis/go-redis/v9 v9.6.1
github.com/prometheus/client_golang v1.19.1
github.com/redis/go-redis/v9 v9.5.3
github.com/robfig/cron/v3 v3.0.1
github.com/samedi/caldav-go v3.0.0+incompatible
github.com/spf13/afero v1.11.0
@@ -65,17 +66,16 @@ require (
github.com/swaggo/swag v1.16.3
github.com/tkuchiki/go-timezone v0.2.3
github.com/typesense/typesense-go v1.1.0
github.com/typesense/typesense-go/v2 v2.0.0
github.com/ulule/limiter/v3 v3.11.2
github.com/wneessen/go-mail v0.4.4
github.com/wneessen/go-mail v0.4.2
github.com/yuin/goldmark v1.7.4
golang.org/x/crypto v0.27.0
golang.org/x/image v0.20.0
golang.org/x/oauth2 v0.23.0
golang.org/x/sync v0.8.0
golang.org/x/sys v0.25.0
golang.org/x/term v0.24.0
golang.org/x/text v0.18.0
golang.org/x/crypto v0.25.0
golang.org/x/image v0.18.0
golang.org/x/oauth2 v0.21.0
golang.org/x/sync v0.7.0
golang.org/x/sys v0.22.0
golang.org/x/term v0.22.0
golang.org/x/text v0.16.0
gopkg.in/d4l3k/messagediff.v1 v1.2.1
gopkg.in/yaml.v3 v3.0.1
mvdan.cc/xurls/v2 v2.5.0
@@ -97,12 +97,12 @@ require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
github.com/cenkalti/backoff/v3 v3.2.2 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/go-chi/chi/v5 v5.1.0 // indirect
github.com/go-chi/chi/v5 v5.0.10 // indirect
github.com/go-faster/city v1.0.1 // indirect
github.com/go-faster/errors v0.7.1 // indirect
github.com/go-jose/go-jose/v4 v4.0.2 // indirect
@@ -121,7 +121,7 @@ require (
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.17.9 // indirect
github.com/klauspost/compress v1.17.7 // indirect
github.com/laurent22/ical-go v0.1.1-0.20181107184520-7e5d6ade8eef // indirect
github.com/lithammer/shortuuid/v3 v3.0.7 // indirect
github.com/magiconair/properties v1.8.7 // indirect
@@ -132,7 +132,6 @@ require (
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/oapi-codegen/runtime v1.1.1 // indirect
github.com/oklog/ulid v1.3.1 // indirect
github.com/onsi/ginkgo v1.16.4 // indirect
@@ -142,16 +141,16 @@ require (
github.com/pierrec/lz4/v4 v4.1.21 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.55.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.48.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/segmentio/asm v1.2.0 // indirect
github.com/shopspring/decimal v1.4.0 // indirect
github.com/sony/gobreaker v1.0.0 // indirect
github.com/sony/gobreaker v0.5.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
@@ -170,7 +169,7 @@ require (
golang.org/x/net v0.27.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
google.golang.org/protobuf v1.34.2 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
@@ -180,4 +179,4 @@ replace github.com/samedi/caldav-go => github.com/kolaente/caldav-go v3.0.1-0.20
go 1.22
toolchain go1.23.1
toolchain go1.22.5
+30 -53
View File
@@ -1,7 +1,7 @@
code.vikunja.io/web v0.0.0-20210706160506-d85def955bd3 h1:MXl7Ff9a/ndTpuEmQKIGhqReE9hWhD4T/+AzK4AXUYc=
code.vikunja.io/web v0.0.0-20210706160506-d85def955bd3/go.mod h1:OgFO06HN1KpA4S7Dw/QAIeygiUPSeGJJn1ykz/sjZdU=
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s=
@@ -18,17 +18,17 @@ github.com/ClickHouse/clickhouse-go/v2 v2.24.0/go.mod h1:iDTViXk2Fgvf1jn2dbJd1ys
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
github.com/ThreeDotsLabs/watermill v1.3.5 h1:50JEPEhMGZQMh08ct0tfO1PsgMOAOhV3zxK2WofkbXg=
github.com/ThreeDotsLabs/watermill v1.3.5/go.mod h1:O/u/Ptyrk5MPTxSeWM5vzTtZcZfxXfO9PK9eXTYiFZY=
github.com/ThreeDotsLabs/watermill v1.3.7 h1:NV0PSTmuACVEOV4dMxRnmGXrmbz8U83LENOvpHekN7o=
github.com/ThreeDotsLabs/watermill v1.3.7/go.mod h1:lBnrLbxOjeMRgcJbv+UiZr8Ylz8RkJ4m6i/VN/Nk+to=
github.com/adlio/trello v1.12.0 h1:JqOE2GFHQ9YtEviRRRSnicSxPbt4WFOxhqXzjMOw8lw=
github.com/adlio/trello v1.12.0/go.mod h1:I4Lti4jf2KxjTNgTqs5W3lLuE78QZZdYbbPnQQGwjOo=
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
github.com/arran4/golang-ical v0.3.1 h1:v13B3eQZ9VDHTAvT6M11vVzxYgcYmjyPBE2eAZl3VZk=
@@ -56,8 +56,6 @@ github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTx
github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY=
github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic=
github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
@@ -79,8 +77,10 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/denisenkom/go-mssqldb v0.12.3 h1:pBSGx9Tq67pBOTLmxNuirNTeB8Vjmf886Kx+8Y+8shw=
github.com/denisenkom/go-mssqldb v0.12.3/go.mod h1:k0mtMFOnU+AihqFxPMiF05rtiDrorD1Vrm1KEz5hxDo=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
@@ -99,18 +99,12 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/gabriel-vasile/mimetype v1.4.4 h1:QjV6pZ7/XZ7ryI2KuyeEDE8wnh7fHP9YnQy+R0LnH8I=
github.com/gabriel-vasile/mimetype v1.4.4/go.mod h1:JwLei5XPtWdGiMFB5Pjle1oEeoSeEuJfJE+TtfvdB/s=
github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4=
github.com/gabriel-vasile/mimetype v1.4.5/go.mod h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4=
github.com/ganigeorgiev/fexpr v0.4.1 h1:hpUgbUEEWIZhSDBtf4M9aUNfQQ0BZkGRaMePy7Gcx5k=
github.com/ganigeorgiev/fexpr v0.4.1/go.mod h1:RyGiGqmeXhEQ6+mlGdnUleLHgtzzu/VGO2WtJkF5drE=
github.com/getsentry/sentry-go v0.28.1 h1:zzaSm/vHmGllRM6Tpx1492r0YDzauArdBfkJRtY6P5k=
github.com/getsentry/sentry-go v0.28.1/go.mod h1:1fQZ+7l7eeJ3wYi82q5Hg8GqAPgefRq+FP/QhafYVgg=
github.com/getsentry/sentry-go v0.29.0 h1:YtWluuCFg9OfcqnaujpY918N/AhCCwarIDWOYSBAjCA=
github.com/getsentry/sentry-go v0.29.0/go.mod h1:jhPesDAL0Q0W2+2YEuVOvdWmVtdsr1+jtBrlDEVWwLY=
github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk=
github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
github.com/go-faster/city v1.0.1 h1:4WAxSZ3V2Ws4QRDrscLEDcibJY8uf41H6AhXDrNDcGw=
@@ -134,6 +128,7 @@ github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
github.com/go-openapi/swag v0.22.8 h1:/9RjDSQ0vbFR+NyjGMkFTsA1IA0fmhKSThmfGZjicbw=
github.com/go-openapi/swag v0.22.8/go.mod h1:6QT22icPLEqAM/z/TChgb4WAveCHF92+2gF0CNjHpPI=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
@@ -284,8 +279,6 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg=
github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/kolaente/caldav-go v3.0.1-0.20190610114120-2a4eb8b5dcc9+incompatible h1:q7DbyV+sFjEoTuuUdRDNl2nlyfztkZgxVVCV7JhzIkY=
github.com/kolaente/caldav-go v3.0.1-0.20190610114120-2a4eb8b5dcc9+incompatible/go.mod h1:y1UhTNI4g0hVymJrI6yJ5/ohy09hNBeU8iJEZjgdDOw=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@@ -300,8 +293,10 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/labstack/echo-jwt/v4 v4.2.0 h1:odSISV9JgcSCuhgQSV/6Io3i7nUmfM/QkBeR5GVJj5c=
github.com/labstack/echo-jwt/v4 v4.2.0/go.mod h1:MA2RqdXdEn4/uEglx0HcUOgQSyBaTh5JcaHIan3biwU=
github.com/labstack/echo/v4 v4.1.16/go.mod h1:awO+5TzAjvL8XpibdsfXxPgHr+orhtXZJZIQCVjogKI=
github.com/labstack/echo/v4 v4.12.0 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+kI0=
github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM=
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
github.com/laurent22/ical-go v0.1.1-0.20181107184520-7e5d6ade8eef h1:RZnRnSID1skF35j/15KJ6hKZkdIC/teQClJK5wP5LU4=
@@ -309,6 +304,7 @@ github.com/laurent22/ical-go v0.1.1-0.20181107184520-7e5d6ade8eef/go.mod h1:4LAT
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
@@ -327,11 +323,15 @@ github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJ
github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A=
github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
@@ -339,12 +339,11 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mattn/go-sqlite3 v1.14.23 h1:gbShiuAP1W5j9UOksQ06aiiqPMxYecovVGwmTxWtuw0=
github.com/mattn/go-sqlite3 v1.14.23/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
@@ -356,8 +355,6 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
@@ -401,28 +398,14 @@ github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg=
github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
github.com/prometheus/client_golang v1.20.2 h1:5ctymQzZlyOON1666svgwn3s6IKWgfbjsejTMiXIyjg=
github.com/prometheus/client_golang v1.20.2/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_golang v1.20.3 h1:oPksm4K8B+Vt35tUhw6GbSNSgVlVSBH0qELP/7u83l4=
github.com/prometheus/client_golang v1.20.3/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_golang v1.20.4 h1:Tgh3Yr67PaOv/uTqloMsCEdeuFTatm5zIq5+qNN23vI=
github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/redis/go-redis/v9 v9.5.3 h1:fOAp1/uJG+ZtcITgZOfYFmTKPE7n4Vclj1wZFgRciUU=
github.com/redis/go-redis/v9 v9.5.3/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
github.com/redis/go-redis/v9 v9.6.1 h1:HHDteefn6ZkTtY5fGUE8tj8uy85AHk6zP7CpzIAM0y4=
github.com/redis/go-redis/v9 v9.6.1/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
@@ -456,8 +439,6 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg=
github.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
github.com/sony/gobreaker v1.0.0 h1:feX5fGGXSl3dYd4aHZItw+FpHLvvoaqkawKjVNiFMNQ=
github.com/sony/gobreaker v1.0.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
@@ -503,17 +484,19 @@ github.com/tkuchiki/go-timezone v0.2.3 h1:D3TVdIPrFsu9lxGxqNX2wsZwn1MZtTqTW0mdev
github.com/tkuchiki/go-timezone v0.2.3/go.mod h1:oFweWxYl35C/s7HMVZXiA19Jr9Y0qJHMaG/J2TES4LY=
github.com/typesense/typesense-go v1.1.0 h1:QocehDarVXRArMIosPIdawiVFZZbnRkPJxwnAGOFkzw=
github.com/typesense/typesense-go v1.1.0/go.mod h1:KcPODU7ltrcUFC/gygMTkAAfZ9M8/q6ayrdl1MnE1kI=
github.com/typesense/typesense-go/v2 v2.0.0/go.mod h1:7V1ZBSfmdciL6yb2bPtWha+W53gV5WZhyOSpVgDJfao=
github.com/ulule/limiter/v3 v3.11.2 h1:P4yOrxoEMJbOTfRJR2OzjL90oflzYPPmWg+dvwN2tHA=
github.com/ulule/limiter/v3 v3.11.2/go.mod h1:QG5GnFOCV+k7lrL5Y8kgEeeflPH3+Cviqlqa8SVSQxI=
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
github.com/valyala/fasttemplate v1.1.0/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
github.com/valyala/fasttemplate v1.2.0/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/wneessen/go-mail v0.4.4 h1:rI8wJzPYymUpUth87vFV3k313bmnid4v+FwhBAYYLFM=
github.com/wneessen/go-mail v0.4.4/go.mod h1:zxOlafWCP/r6FEhAaRgH4IC1vg2YXxO0Nar9u0IScZ8=
github.com/wneessen/go-mail v0.4.2 h1:wISuU9LOGqrA7pxy7OipRtwoExXTzuGKmAjb8gYwc00=
github.com/wneessen/go-mail v0.4.2/go.mod h1:zxOlafWCP/r6FEhAaRgH4IC1vg2YXxO0Nar9u0IScZ8=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
@@ -548,11 +531,14 @@ go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
@@ -562,15 +548,11 @@ golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
golang.org/x/image v0.20.0 h1:7cVCUjQwfL18gyBJOmYvptfSHS8Fb3YUDtfLIZ7Nbpw=
golang.org/x/image v0.20.0/go.mod h1:0a88To4CYVBAHp5FXJm8o7QbUl37Vd85ply1vyD8auM=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
@@ -579,13 +561,17 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
@@ -598,8 +584,6 @@ golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -608,8 +592,6 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -625,6 +607,7 @@ golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200808120158-1030fc2bf1d9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -639,16 +622,12 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk=
golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@@ -658,8 +637,6 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
@@ -697,8 +674,6 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -774,9 +749,11 @@ src.techknowlogick.com/xgo v1.7.1-0.20240403232151-e01c4fbef884 h1:Ttvt8FCpUXfC8
src.techknowlogick.com/xgo v1.7.1-0.20240403232151-e01c4fbef884/go.mod h1:31CE1YKtDOrKTk9PSnjTpe6YbO6W/0LTYZ1VskL09oU=
src.techknowlogick.com/xormigrate v1.7.1 h1:RKGLLUAqJ+zO8iZ7eOc7oLH7f0cs2gfXSZSvBRBHnlY=
src.techknowlogick.com/xormigrate v1.7.1/go.mod h1:YGNBdj8prENlySwIKmfoEXp7ILGjAltyKFXD0qLgD7U=
xorm.io/builder v0.3.7/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
xorm.io/builder v0.3.13 h1:a3jmiVVL19psGeXx8GIurTp7p0IIgqeDmwhcR6BAOAo=
xorm.io/builder v0.3.13/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
xorm.io/xorm v1.0.5/go.mod h1:uF9EtbhODq5kNWxMbnBEj8hRRZnlcNSz2t2N7HW/+A4=
xorm.io/xorm v1.3.3/go.mod h1:qFJGFoVYbbIdnz2vaL5OxSQ2raleMpyRRalnq3n9OJo=
xorm.io/xorm v1.3.9 h1:TUovzS0ko+IQ1XnNLfs5dqK1cJl1H5uHpWbWqAQ04nU=
xorm.io/xorm v1.3.9/go.mod h1:LsCCffeeYp63ssk0pKumP6l96WZcHix7ChpurcLNuMw=
+1 -21
View File
@@ -464,26 +464,6 @@ func (Build) Build() {
runAndStreamOutput("go", "build", Goflags[0], "-tags", Tags, "-ldflags", "-s -w "+Ldflags, "-o", Executable)
}
func (Build) SaveVersionToFile() error {
// Open the file for writing. If the file doesn't exist, create it.
// If it exists, truncate it.
file, err := os.Create("VERSION")
if err != nil {
return fmt.Errorf("error creating VERSION file: %w", err)
}
defer file.Close()
// Write the version number to the file
_, err = file.WriteString(VersionNumber)
if err != nil {
return fmt.Errorf("error writing to VERSION file: %w", err)
}
fmt.Println("Version number saved successfully to VERSION file")
return nil
}
type Release mg.Namespace
// Runs all steps in the right order to create release packages for various platforms
@@ -813,7 +793,7 @@ func init() {
ID: "` + date + `",
Description: "",
Migrate: func(tx *xorm.Engine) error {
return tx.Sync(` + str + date + `{})
return tx.Sync2(` + str + date + `{})
},
Rollback: func(tx *xorm.Engine) error {
return nil
+1 -2
View File
@@ -92,8 +92,7 @@ func getCaldavColor(color string) (caldavcolor string) {
return `
X-APPLE-CALENDAR-COLOR:` + color + `
X-OUTLOOK-COLOR:` + color + `
X-FUNAMBOL-COLOR:` + color + `
COLOR:` + color
X-FUNAMBOL-COLOR:` + color
}
func formatDuration(duration time.Duration) string {

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