Compare commits

..

60 Commits

Author SHA1 Message Date
J. Lavoie 69590246df
Translated using Weblate (French)
Currently translated at 100.0% (666 of 666 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/fr/
2021-07-12 20:52:52 +02:00
J. Lavoie b203e4a169
Translated using Weblate (German)
Currently translated at 96.6% (644 of 666 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/de/
2021-07-12 20:52:51 +02:00
J. Lavoie c5f8ed629a
Translated using Weblate (French)
Currently translated at 100.0% (666 of 666 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/fr/
2021-07-10 12:40:09 +02:00
J. Lavoie 08ed54ab4e
Translated using Weblate (German)
Currently translated at 93.9% (626 of 666 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/de/
2021-07-10 12:40:06 +02:00
kolaente 2370115c35
Format 2021-07-09 10:31:30 +02:00
kolaente 86ca6c29c5
Merge branch 'main' into translations
# Conflicts:
#	src/i18n/lang/en.json
2021-07-09 10:31:13 +02:00
Andrey Kashlak f6b4b44743
Translated using Weblate (Russian)
Currently translated at 92.6% (581 of 627 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/ru/
2021-07-09 09:56:24 +02:00
Sergio b54fae513a
Translated using Weblate (Spanish)
Currently translated at 8.6% (54 of 627 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/es/
2021-07-09 09:56:23 +02:00
Jesse James Isler d350b02aca
Translated using Weblate (German)
Currently translated at 100.0% (627 of 627 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/de/
2021-07-09 09:56:23 +02:00
Jesse James Isler 1238528e2b
Translated using Weblate (English)
Currently translated at 98.7% (619 of 627 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/en/
2021-07-09 09:56:12 +02:00
Andrey Kashlak 8de194bc06
Translated using Weblate (English)
Currently translated at 98.7% (619 of 627 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/en/
2021-07-09 09:56:12 +02:00
Andrey Kashlak 9800e1701d
Translated using Weblate (Russian)
Currently translated at 90.1% (565 of 627 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/ru/
2021-07-06 16:18:50 +02:00
Andrey Kashlak 8e6633f70f
Translated using Weblate (Russian)
Currently translated at 75.9% (476 of 627 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/ru/
2021-07-06 15:04:55 +02:00
Andrey Kashlak eaca985d44
Translated using Weblate (Russian)
Currently translated at 73.6% (462 of 627 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/ru/
2021-07-06 14:56:15 +02:00
Andrey Kashlak 1d5def2d8e
Translated using Weblate (Russian)
Currently translated at 71.6% (449 of 627 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/ru/
2021-07-06 14:40:26 +02:00
Andrey Kashlak 7d5077cd8f
Translated using Weblate (Russian)
Currently translated at 53.5% (336 of 627 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/ru/
2021-07-06 13:16:31 +02:00
Jesse James Isler b24365640f
Translated using Weblate (German)
Currently translated at 51.1% (321 of 627 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/de/
2021-07-06 13:16:30 +02:00
J. Lavoie 024af54cd1
Translated using Weblate (German)
Currently translated at 48.0% (301 of 627 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/de/
2021-07-03 02:38:09 +02:00
Andrey Kashlak 5cf1fb831a
Translated using Weblate (Russian)
Currently translated at 51.3% (322 of 627 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/ru/
2021-07-02 00:33:16 +02:00
J. Lavoie eb6ade1fac
Translated using Weblate (German)
Currently translated at 47.6% (299 of 627 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/de/
2021-07-02 00:33:15 +02:00
J. Lavoie 49fa8dd5ff
Translated using Weblate (French)
Currently translated at 100.0% (627 of 627 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/fr/
2021-06-30 23:17:04 +02:00
Allan Nordhøy 004484fbd7
Translated using Weblate (French)
Currently translated at 73.8% (463 of 627 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/fr/
2021-06-30 00:24:37 +02:00
Luis 86e9cfdf4b
Translated using Weblate (Spanish)
Currently translated at 7.8% (49 of 627 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/es/
2021-06-30 00:24:36 +02:00
Allan Nordhøy 32dd9bf138
Translated using Weblate (German)
Currently translated at 47.2% (296 of 627 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/de/
2021-06-30 00:24:36 +02:00
Allan Nordhøy 7476949852
Translated using Weblate (Norwegian Bokmål)
Currently translated at 77.9% (489 of 627 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/nb_NO/
2021-06-30 00:24:36 +02:00
Allan Nordhøy f7e24f9df3
Translated using Weblate (English)
Currently translated at 98.5% (618 of 627 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/en/
2021-06-30 00:24:36 +02:00
kolaente 82b756cd99
Add pt translation file 2021-06-30 00:24:21 +02:00
Allan Nordhøy 6ade8c6607
Translated using Weblate (Norwegian Bokmål)
Currently translated at 41.9% (263 of 627 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/nb_NO/
2021-06-28 20:37:30 +02:00
Allan Nordhøy da71cf7220
Translated using Weblate (Russian)
Currently translated at 56.4% (354 of 627 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/ru/
2021-06-28 20:37:17 +02:00
Allan Nordhøy 4a7d0d5b7b
Translated using Weblate (French)
Currently translated at 86.6% (543 of 627 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/fr/
2021-06-28 20:37:17 +02:00
Allan Nordhøy 76f67f60bc
Translated using Weblate (German)
Currently translated at 52.6% (330 of 627 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/de/
2021-06-28 20:37:17 +02:00
Allan Nordhøy 812d1ba560
Translated using Weblate (English)
Currently translated at 100.0% (627 of 627 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/en/
2021-06-28 20:37:16 +02:00
Allan Nordhøy aef4792be5
Added translation using Weblate (Norwegian Bokmål) 2021-06-28 00:00:38 +02:00
kolaente e2959f210d
Merge branch 'main' into translations 2021-06-27 13:53:16 +02:00
kolaente 33ff902c6c
Remove empty translation strings 2021-06-27 13:49:44 +02:00
kolaente fca4b93002
Fix broken translation key 2021-06-27 13:41:15 +02:00
Luis dc41288ec1
Translated using Weblate (Spanish)
Currently translated at 6.5% (41 of 627 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/es/
2021-06-27 13:40:40 +02:00
Anonymous 2fd47b585d
Translated using Weblate (French)
Currently translated at 97.2% (610 of 627 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/fr/
2021-06-26 00:46:41 +02:00
Anonymous 44a4e08d0d
Translated using Weblate (Russian)
Currently translated at 97.2% (610 of 627 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/ru/
2021-06-26 00:46:33 +02:00
Anonymous db31574858
Translated using Weblate (Romanian)
Currently translated at 97.2% (610 of 627 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/ro/
2021-06-26 00:46:23 +02:00
Anonymous 345f02b66a
Translated using Weblate (Spanish)
Currently translated at 97.2% (610 of 627 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/es/
2021-06-26 00:45:56 +02:00
Anonymous 53a4e463f2
Translated using Weblate (German)
Currently translated at 97.2% (610 of 627 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/de/
2021-06-26 00:45:26 +02:00
Konrad d3586a3d5c
Translated using Weblate (English)
Currently translated at 100.0% (627 of 627 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/en/
2021-06-26 00:38:30 +02:00
kolaente 3aa8488dc4
Fix labels translation string in filters 2021-06-26 00:31:27 +02:00
Swann Fournial b0827e2ba8
Translated using Weblate (French)
Currently translated at 96.8% (607 of 627 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/fr/
2021-06-25 23:46:45 +02:00
Swann Fournial 4123d739d9
Translated using Weblate (German)
Currently translated at 60.1% (377 of 627 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/de/
2021-06-25 23:46:45 +02:00
Andrey Kashlak d55fdbf223
Translated using Weblate (Russian)
Currently translated at 64.4% (404 of 627 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/ru/
2021-06-25 18:30:29 +02:00
Andrey Kashlak 2b8884c39a
Translated using Weblate (Russian)
Currently translated at 64.1% (402 of 627 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/ru/
2021-06-25 18:25:28 +02:00
Andrey Kashlak b93d853022
Translated using Weblate (Russian)
Currently translated at 63.1% (396 of 627 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/ru/
2021-06-25 18:19:51 +02:00
Andrey Kashlak 3db06bc81b
Translated using Weblate (Russian)
Currently translated at 59.3% (372 of 627 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/ru/
2021-06-25 18:09:18 +02:00
Andrey Kashlak 3416c2598e
Translated using Weblate (Russian)
Currently translated at 57.2% (359 of 627 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/ru/
2021-06-25 18:01:18 +02:00
Andrey Kashlak 2d754f0aac
Translated using Weblate (Russian)
Currently translated at 46.7% (293 of 627 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/ru/
2021-06-25 17:42:37 +02:00
Swann Fournial 01669831e5
Translated using Weblate (French)
Currently translated at 96.8% (607 of 627 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/fr/
2021-06-25 17:08:53 +02:00
Andrey Kashlak b25cea2180
Translated using Weblate (Russian)
Currently translated at 44.4% (279 of 627 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/ru/
2021-06-25 17:08:53 +02:00
Swann Fournial be86427374
Translated using Weblate (German)
Currently translated at 35.8% (225 of 627 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/de/
2021-06-25 17:08:53 +02:00
Konrad 4dbec1acab
Translated using Weblate (English)
Currently translated at 99.8% (626 of 627 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/en/
2021-06-25 17:08:53 +02:00
kolaente e096de57d3
Merge branch 'main' into translations 2021-06-25 14:09:54 +02:00
Swann Fournial 4ba6261549
Translated using Weblate (French)
Currently translated at 96.0% (602 of 627 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/fr/
2021-06-25 07:43:39 +02:00
Nathan a707931c55
Translated using Weblate (French)
Currently translated at 0.1% (1 of 627 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/fr/
2021-06-24 13:31:37 +02:00
Andrey Kashlak 44bdbd2fdb
Translated using Weblate (Russian)
Currently translated at 0.1% (1 of 627 strings)

Translation: Vikunja/Frontend
Translate-URL: https://hosted.weblate.org/projects/vikunja/frontend/ru/
2021-06-24 13:31:37 +02:00
567 changed files with 28750 additions and 45989 deletions

View File

@ -1,4 +1,3 @@
---
kind: pipeline
name: build
@ -13,140 +12,118 @@ trigger:
services:
- name: api
image: vikunja/api:unstable
image: vikunja/api
environment:
VIKUNJA_SERVICE_TESTINGTOKEN: averyLongSecretToSe33dtheDB
VIKUNJA_LOG_LEVEL: DEBUG
steps:
# Disabled until we figure out why it is so slow
# - name: restore-cache
# image: meltwater/drone-cache:dev
# pull: true
# environment:
# AWS_ACCESS_KEY_ID:
# from_secret: cache_aws_access_key_id
# AWS_SECRET_ACCESS_KEY:
# from_secret: cache_aws_secret_access_key
# settings:
# restore: true
# bucket: kolaente.dev-drone-dependency-cache
# endpoint: https://s3.fr-par.scw.cloud
# region: fr-par
# path_style: true
# cache_key: '{{ .Repo.Name }}_{{ checksum "yarn.lock" }}_{{ arch }}_{{ os }}'
# mount:
# - '.cache'
- name: restore-cache
image: meltwater/drone-cache:dev
pull: true
environment:
AWS_ACCESS_KEY_ID:
from_secret: cache_aws_access_key_id
AWS_SECRET_ACCESS_KEY:
from_secret: cache_aws_secret_access_key
settings:
restore: true
bucket: kolaente.dev-drone-dependency-cache
endpoint: https://s3.fr-par.scw.cloud
region: fr-par
path_style: true
cache_key: '{{ .Repo.Name }}_{{ checksum "yarn.lock" }}_{{ arch }}_{{ os }}'
mount:
- '.cache'
- name: dependencies
image: node:18
image: node:16
pull: true
environment:
YARN_CACHE_FOLDER: .cache/yarn/
CYPRESS_CACHE_FOLDER: .cache/cypress/
commands:
- yarn --frozen-lockfile --network-timeout 100000
# depends_on:
# - restore-cache
depends_on:
- restore-cache
# - name: rebuild-cache
# image: meltwater/drone-cache:dev
# pull: true
# environment:
# AWS_ACCESS_KEY_ID:
# from_secret: cache_aws_access_key_id
# AWS_SECRET_ACCESS_KEY:
# from_secret: cache_aws_secret_access_key
# settings:
# rebuild: true
# bucket: kolaente.dev-drone-dependency-cache
# endpoint: https://s3.fr-par.scw.cloud
# region: fr-par
# path_style: true
# cache_key: '{{ .Repo.Name }}_{{ checksum "yarn.lock" }}_{{ arch }}_{{ os }}'
# mount:
# - '.cache'
# depends_on:
# - dependencies
- name: rebuild-cache
image: meltwater/drone-cache:dev
pull: true
environment:
AWS_ACCESS_KEY_ID:
from_secret: cache_aws_access_key_id
AWS_SECRET_ACCESS_KEY:
from_secret: cache_aws_secret_access_key
settings:
rebuild: true
bucket: kolaente.dev-drone-dependency-cache
endpoint: https://s3.fr-par.scw.cloud
region: fr-par
path_style: true
cache_key: '{{ .Repo.Name }}_{{ checksum "yarn.lock" }}_{{ arch }}_{{ os }}'
mount:
- '.cache'
depends_on:
- dependencies
- name: lint
image: node:18
- name: build
image: node:16
pull: true
environment:
YARN_CACHE_FOLDER: .cache/yarn/
CYPRESS_CACHE_FOLDER: .cache/cypress/
commands:
- yarn run lint
depends_on:
- dependencies
- name: build-prod
image: node:18
pull: true
environment:
YARN_CACHE_FOLDER: .cache/yarn/
commands:
- yarn build
- yarn run build
depends_on:
- dependencies
- name: test-unit
image: node:18
image: node:16
pull: true
commands:
- yarn test:unit
depends_on:
- dependencies
- name: typecheck
failure: ignore
image: node:18
pull: true
commands:
- yarn typecheck
depends_on:
- dependencies
- name: test-frontend
image: cypress/browsers:node16.5.0-chrome94-ff93
image: cypress/browsers:node12.18.3-chrome87-ff82
pull: true
environment:
CYPRESS_API_URL: http://api:3456/api/v1
CYPRESS_TEST_SECRET: averyLongSecretToSe33dtheDB
YARN_CACHE_FOLDER: .cache/yarn/
CYPRESS_CACHE_FOLDER: .cache/cypress/
CYPRESS_DEFAULT_COMMAND_TIMEOUT: 60000
CYPRESS_RECORD_KEY:
from_secret: cypress_project_key
CYPRESS_DEFAULT_COMMAND_TIMEOUT: 20000
commands:
- sed -i 's/localhost/api/g' dist/index.html
- yarn serve:dist & npx wait-on http://localhost:4173
- yarn test:frontend --browser chrome --record
- sed -i 's/localhost/api/g' public/index.html
- yarn serve & npx wait-on http://localhost:8080
- yarn test:frontend --browser chrome
depends_on:
- build-prod
- dependencies
- name: deploy-preview
image: node:18
- name: upload-test-results
image: plugins/s3:1
pull: true
environment:
NETLIFY_AUTH_TOKEN:
from_secret: netlify_auth_token
NETLIFY_SITE_ID:
from_secret: netlify_site_id
GITEA_TOKEN:
from_secret: gitea_token
commands:
- cp -r dist dist-preview
# Override the default api url used for preview
- sed -i 's|localhost:3456|try.vikunja.io|g' dist-preview/index.html
- shasum -a 384 -c ./scripts/deploy-preview-netlify.js.sha384
- node ./scripts/deploy-preview-netlify.js
settings:
bucket: drone-test-results
access_key:
from_secret: test_results_aws_access_key_id
secret_key:
from_secret: test_results_aws_secret_access_key
endpoint: https://s3.fr-par.scw.cloud
region: fr-par
path_style: true
source: cypress/screenshots/**/**/*
strip_prefix: cypress/screenshots/
target: /${DRONE_REPO}/${DRONE_PULL_REQUEST}_${DRONE_BRANCH}/${DRONE_BUILD_NUMBER}/
depends_on:
- build-prod
- test-frontend
when:
event:
include:
- pull_request
status:
- failure
- success
---
kind: pipeline
@ -167,26 +144,26 @@ steps:
commands:
- git fetch --tags
# - name: restore-cache
# image: meltwater/drone-cache:dev
# pull: true
# environment:
# AWS_ACCESS_KEY_ID:
# from_secret: cache_aws_access_key_id
# AWS_SECRET_ACCESS_KEY:
# from_secret: cache_aws_secret_access_key
# settings:
# restore: true
# bucket: kolaente.dev-drone-dependency-cache
# endpoint: https://s3.fr-par.scw.cloud
# region: fr-par
# path_style: true
# cache_key: '{{ .Repo.Name }}_{{ checksum "yarn.lock" }}_{{ arch }}_{{ os }}'
# mount:
# - '.cache'
- name: restore-cache
image: meltwater/drone-cache:dev
pull: true
environment:
AWS_ACCESS_KEY_ID:
from_secret: cache_aws_access_key_id
AWS_SECRET_ACCESS_KEY:
from_secret: cache_aws_secret_access_key
settings:
restore: true
bucket: kolaente.dev-drone-dependency-cache
endpoint: https://s3.fr-par.scw.cloud
region: fr-par
path_style: true
cache_key: '{{ .Repo.Name }}_{{ checksum "yarn.lock" }}_{{ arch }}_{{ os }}'
mount:
- '.cache'
- name: build
image: node:18
image: node:16
pull: true
group: build-static
environment:
@ -197,8 +174,8 @@ steps:
- "echo '{\"VERSION\": \"'$(git describe --tags --always --abbrev=10 | sed 's/-/+/' | sed 's/^v//' | sed 's/-g/-/')'\"}' > src/version.json"
- yarn run build
- sed -i 's/http\:\\/\\/localhost\\:3456\\/api\\/v1/\\/api\\/v1/g' dist/index.html # Override the default api url used for developing
# depends_on:
# - restore-cache
depends_on:
- restore-cache
- name: static
image: kolaente/zip
@ -210,7 +187,7 @@ steps:
depends_on: [ build ]
- name: release
image: plugins/s3
image: plugins/s3:1
pull: true
settings:
bucket: vikunja-releases
@ -242,26 +219,26 @@ steps:
commands:
- git fetch --tags
# - name: restore-cache
# image: meltwater/drone-cache:dev
# pull: true
# environment:
# AWS_ACCESS_KEY_ID:
# from_secret: cache_aws_access_key_id
# AWS_SECRET_ACCESS_KEY:
# from_secret: cache_aws_secret_access_key
# settings:
# restore: true
# bucket: kolaente.dev-drone-dependency-cache
# endpoint: https://s3.fr-par.scw.cloud
# region: fr-par
# path_style: true
# cache_key: '{{ .Repo.Name }}_{{ checksum "yarn.lock" }}_{{ arch }}_{{ os }}'
# mount:
# - '.cache'
- name: restore-cache
image: meltwater/drone-cache:dev
pull: true
environment:
AWS_ACCESS_KEY_ID:
from_secret: cache_aws_access_key_id
AWS_SECRET_ACCESS_KEY:
from_secret: cache_aws_secret_access_key
settings:
restore: true
bucket: kolaente.dev-drone-dependency-cache
endpoint: https://s3.fr-par.scw.cloud
region: fr-par
path_style: true
cache_key: '{{ .Repo.Name }}_{{ checksum "yarn.lock" }}_{{ arch }}_{{ os }}'
mount:
- '.cache'
- name: build
image: node:18
image: node:16
pull: true
group: build-static
environment:
@ -272,8 +249,8 @@ steps:
- "echo '{\"VERSION\": \"'$(git describe --tags --always --abbrev=10 | sed 's/-/+/' | sed 's/^v//' | sed 's/-g/-/')'\"}' > src/version.json"
- yarn run build
- sed -i 's/http\:\\/\\/localhost\\:3456\\/api\\/v1/\\/api\\/v1/g' dist/index.html # Override the default api url used for developing
# depends_on:
# - restore-cache
depends_on:
- restore-cache
- name: static
image: kolaente/zip
@ -285,7 +262,7 @@ steps:
depends_on: [ build ]
- name: release
image: plugins/s3
image: plugins/s3:1
pull: true
settings:
bucket: vikunja-releases
@ -311,7 +288,7 @@ trigger:
- push
depends_on:
- release-latest
- release-latest
steps:
- name: trigger
@ -340,12 +317,9 @@ trigger:
ref:
- refs/heads/main
- "refs/tags/**"
event:
exclude:
- cron
steps:
- name: docker-unstable
- name: docker-latest
image: plugins/docker:linux-arm
pull: true
settings:
@ -354,7 +328,7 @@ steps:
password:
from_secret: docker_password
repo: vikunja/frontend
tags: unstable-linux-arm
tags: latest-linux-arm
build_args:
- USE_RELEASE=true
- RELEASE_VERSION=unstable
@ -384,7 +358,7 @@ steps:
depends_on:
- clone
- name: docker-unstable-arm64
- name: docker-latest-arm64
image: plugins/docker:linux-arm64
pull: true
settings:
@ -393,7 +367,7 @@ steps:
password:
from_secret: docker_password
repo: vikunja/frontend
tags: unstable-linux-arm64
tags: latest-linux-arm64
build_args:
- USE_RELEASE=true
- RELEASE_VERSION=unstable
@ -440,12 +414,9 @@ trigger:
ref:
- refs/heads/main
- "refs/tags/**"
event:
exclude:
- cron
steps:
- name: docker-unstable
- name: docker-latest
image: plugins/docker:linux-amd64
pull: true
settings:
@ -454,7 +425,7 @@ steps:
password:
from_secret: docker_password
repo: vikunja/frontend
tags: unstable-linux-amd64
tags: latest-linux-amd64
build_args:
- USE_RELEASE=true
- RELEASE_VERSION=unstable
@ -489,21 +460,18 @@ trigger:
ref:
- refs/heads/main
- "refs/tags/**"
event:
exclude:
- cron
depends_on:
- docker-amd64-release
- docker-arm-release
steps:
- name: manifest-unstable
- name: manifest-latest
pull: always
image: plugins/manifest
settings:
tags: unstable
spec: docker-manifest-unstable.tmpl
tags: latest
spec: docker-manifest-latest.tmpl
password:
from_secret: docker_password
username:
@ -526,23 +494,6 @@ steps:
when:
ref:
- "refs/tags/**"
- name: manifest-release-latest
pull: always
image: plugins/manifest
depends_on:
- clone
settings:
tags: latest
ignore_missing: true
spec: docker-manifest.tmpl
password:
from_secret: docker_password
username:
from_secret: docker_username
when:
ref:
- "refs/tags/**"
---
kind: pipeline
@ -553,9 +504,6 @@ trigger:
ref:
- refs/heads/main
- "refs/tags/**"
event:
exclude:
- cron
depends_on:
- build
@ -582,8 +530,7 @@ steps:
- failure
---
kind: pipeline
type: docker
name: update-translations
name: ping-weblate
depends_on:
- build
@ -592,61 +539,23 @@ trigger:
branch:
- main
event:
- cron
cron:
- update_translations
- push
steps:
- name: download
pull: always
image: jonasfranz/crowdin
settings:
download: true
export_dir: src/i18n/lang/
ignore_branch: true
project_identifier: vikunja
environment:
CROWDIN_KEY:
from_secret: crowdin_key
- name: move-files
pull: always
image: bash
depends_on:
- download
commands:
- mv src/i18n/lang/*/*.json src/i18n/lang
- name: push
pull: always
- name: update-translation-base
image: appleboy/drone-git-push
depends_on:
- move-files
failure: ignore
settings:
author_email: "frederik@vikunja.io"
author_name: Frederick [Bot]
branch: main
commit: true
commit_message: "[skip ci] Updated translations via Crowdin"
remote: "ssh://git@kolaente.dev:9022/vikunja/frontend.git"
branch: translations
remote: ssh://git@kolaente.dev:9022/vikunja/frontend.git
ssh_key:
from_secret: translation_git_push_ssh_key
- name: upload
pull: always
image: jonasfranz/crowdin
from_secret: translations_branch_update_ssh_key
- name: notify-weblate
image: curlimages/curl
depends_on:
- clone
settings:
files:
en.json: src/i18n/lang/en.json
ignore_branch: true
project_identifier: vikunja
- update-translation-base
environment:
CROWDIN_KEY:
from_secret: crowdin_key
---
kind: signature
hmac: 997e1badebe484ac29557c4af356e63db4d3d57f3d32e92d482f117f8cec64da
...
WEBLATE_TOKEN:
from_secret: weblate_token
commands:
- ./ping-weblate.sh

View File

@ -1,29 +0,0 @@
# EditorConfig is awesome: https://EditorConfig.org
# top-most EditorConfig file
root = true
[*]
indent_style = tab
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = false
insert_final_newline = false
[*.vue]
indent_style = tab
[*.{yaml,yml}]
indent_style = space
indent_size = 2
[*.json]
indent_style = space
indent_size = 2
[*.{scss,css}]
indent_style = space
indent_size = 2
[.nvmrc]
insert_final_newline = false

7
.gitignore vendored
View File

@ -1,6 +1,6 @@
.DS_Store
node_modules
/dist*
/dist
*.zip
# local env files
@ -11,7 +11,6 @@ node_modules
npm-debug.log*
yarn-debug.log*
yarn-error.log*
stats.html
# Editor directories and files
.idea
@ -21,11 +20,7 @@ stats.html
*.njsproj
*.sln
*.sw*
!rollup.sw.js
# Test files
cypress/screenshots
cypress/videos
# Local Netlify folder
.netlify

1
.nvmrc
View File

@ -1 +0,0 @@
v18

View File

@ -1,12 +0,0 @@
{
"recommendations": [
"codezombiech.gitignore",
"dbaeumer.vscode-eslint",
"editorconfig.editorconfig",
"johnsoncodehk.volar",
"lokalise.i18n-ally",
"mgmcdermott.vscode-language-babel",
"mikestead.dotenv",
"Syler.sass-indented"
]
}

View File

@ -1,30 +0,0 @@
{
"eslint.packageManager": "yarn",
"editor.formatOnSave": false,
"editor.codeActionsOnSave": {
"source.fixAll": true
},
"eslint.format.enable": true,
"[javascript]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
},
"[typescript]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
},
// https://eslint.vuejs.org/user-guide/#editor-integrations
"eslint.validate": [
"javascript",
"javascriptreact",
"vue"
],
"vetur.validation.template": false,
// i18n ally
"i18n-ally.localesPaths": [
"src/i18n/lang"
],
"i18n-ally.sortKeys": true,
"i18n-ally.keepFulfilled": true,
"i18n-ally.keystyle": "nested",
}

View File

@ -2,354 +2,13 @@
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres
to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
All releases can be found on https://code.vikunja.io/frontend/releases.
The releases aim at the api versions which is why there are missing versions.
## [0.18.2] - 2021-11-23
### Fixed
* fix(docker): properly replace api url
* fix: edit saved filter title
## [0.18.1] - 2021-09-08
### Added
* feat: make it possible to fake online state via dev env (#720)
### Fixed
* fix: call to /null from background image (#714)
* Fix data export download progress
* fix: kanban-card mutatation violation (#712)
* Fix missing translation when creating a new task on the kanban board
* Fix rearranging tasks in a kanban bucket when its limit was reached
* Fix sort order for table view
* Fix task attributes overridden when saving the task title with enter
* Fix translation badge
### Dependency Updates
* Update dependency @4tw/cypress-drag-drop to v2 (#711)
* Update dependency axios to v0.21.4 (#705)
* Update dependency jest to v27.1.1 (#716)
* Update dependency vite-plugin-vue2 to v1.8.2 (#707)
* Update dependency vite to v2.5.4 (#708)
* Update dependency vite to v2.5.5 (#709)
* Update typescript-eslint monorepo to v4.31.0 (#706)
## [0.18.0] - 2021-09-05
### Added
* Add a button to copy an attachment url from the attachment overview
* Add collapsing kanban buckets
* Add confirm with enter when setting a new password
* Add default list setting & creating tasks from home (#520)
* Add depends_on for push step
* Add depends_on for upload step
* Add drag delay on mobile
* Add express for serve:dev
* Add filters for quick action bar
* Add frontend tests for list history
* Add making tasks favorite from the task detail view
* Add missing position property to list and bucket models
* Add more debug logs for gantt charts
* Add more global state tests (#521)
* Add proofread languages to available languages
* Add quick action bar shortcut to shortcut overview
* Add setting for the first day of the week
* Add showing version info in GUI
* Add syncing translations to crowdin
* Add timeout to fix race condition when authenticating as a link share and renewing the token simultaneously
* Add translations (#562)
* Add typescript support for helper functions (#598)
* Add vite (#416)
* Allow failure of the weblate update step
* Always set the kanban board to full width for share links
* Another day, another js date edge-case
* Automatically update approved translations from crowdin
* Break long list titles in list overview
* Preload labels and use locally stored in vuex
* PWA improvments (#622)
* Quick Actions & global search (#528)
* Quick add magic for tasks (#570)
* Reorder tasks, lists and kanban buckets (#620)
* Show last visited list on home page
* Show recently visited lists in quick actions
* Show salutation based on the time of day
* Sort labels alphabetically on tasks
* Switch the :latest docker image tag to contain the latest release instead of the latest unstable
### Changed
* Change building latest docker image
* Change desktop downstream trigger plugin with our own debug build
* Change menu hamburger icon
* Change quick add magic characters to be more familiar with the todoist ones
* Change the docker builder image to a working one on arm
* chore: discard old font file formats (#673)
* chore: only import common languages (#671)
* Cleanup broken sw functions
* Cleanup drone pipeline
* Cleanup old vue cli config
* Configure tests retries
* Decrease page padding on task detail page
* Directly redirect to the openid auth provider if that's the only auth method
* Don't allow dragging a list when the user does not have the rights
* Don't load already loaded task attachments again when saving an edited task description
* Don't prefetch all i18n files
* Don't show archived lists/namespaces in quick actions
* feat: provide global variables in all components (#669)
* Hide favorite list edit menu
* Hide keyboard shortcuts indicator on mobile
* Improve chunk size
* Improve some translations (#581)
* Improve tests
* Indicate done tasks in quick actions
* Load list background in list card
* Make editor edit button at the bottom the default and make sure the done button stands out more
* Make saving a text edit a button
* Make sure highlight.js is always lazy-loaded
* Make sure the task popup view takes up all the space it can on mobile
* Make tests less flaky
* Make the logo smaller on link shared lists
* Make the progress bar color lighter
* Move creation of new items to the bottom of the multiselect list
* Move general settings to the top
* Move translated files after downloading them
* Move weblate ping to shell script
* Only add a drag delay if on mobile instead of setting it to 0
* Only build a bundle for modern browsers
* Refactor success and error messages
* Refactor success and error notifications to prevent html in them
* Remove logout button for link shares
* Run frontend-tests with dist in ci (#605)
* Save auth tokens from link shares only in memory, don't persist them to localStorage
* Search namespaces locally only when duplicating a list
* Show errors from openid provider
* Show labels alphabetically sorted in the overview
* Small cleanups & code improvements
* TOTP UX improvements & translation fixes
### Fixed
* Fix changing the repeat mode of a task when no value is entered yet
* Fix comment on different task after clicking on a task notification
* Fix CTA spacings
* Fix date parsing parsing words with weekdays in them (#607)
* fix(deps): update dependency marked to v3.0.1 (#677)
* fix(deps): update dependency marked to v3.0.2 (#682)
* Fix error property already defined as a function
* Fix flickering pre-loaded search results when focusing the search input
* Fix Gantt layout overflowsing on mobile
* Fix gantt months being wrong
* Fix git push remote to update crowdin translations
* Fix global mutation of has tasks state
* Fix header layout for long list titles
* Fix highlight.js in editor
* Fix home page tests
* Fix keyboard shortcuts not working on the task detail page
* Fix label changes appearing to be saved immediately when editing them
* Fix labels list in saved filter spacing
* Fix lint
* Fix list archived notification mobile layout
* Fix list settings not being available when list backgrounds are disabled
* Fix lists showing up multiple times in history
* Fix llama background url
* Fix loading a list when it was already partially saved in vuex
* Fix loading & disabled state on inputs when creating a new task
* Fix loading labels when editing a saved filter
* Fix menu styles
* Fix missing background for tasks on a shared list with a background
* Fix multiselect search padding
* Fix new lists created with quick actions not showing up in the menu
* fix: non unique ids (#672)
* Fix not reloading tasks of a saved filter after editing it
* Fix not updating list name in store when changing it
* Fix other values getting pushed away when creating a new one through multiselect
* Fix padding for kanban cards
* Fix parsing dates on the last day of the month
* Fix populating task details ater updating the description
* Fix quick actions not opening
* Fix quick actions not working when nonexisting lists where left over in history
* Fix redirecting to /login for some routes
* Fix removing a namespace from state after it was deleted
* Fix resetting date filters from upcoming after viewing a task detail page (popup)
* Fix sass division
* Fix saving showing archived setting
* Fix selecting a single value from multiselect
* Fix sending openid scopes when authenticating
* Fix sending the user back to the list view they came from when opening a task in detail view
* Fix setting a task as favorite button
* Fix setting delete button for newly created task comments
* Fix setting filters for reminders
* Fix setting secret for updating translations
* Fix setting task favorite status in test fixtures
* Fix showing an editor save button in cases where it wasn't required
* Fix showing edit buttons when the user does not have the rights to use them
* Fix showing import tasks cta when tasks are loading
* Fix some translation strings
* Fix sorting labels
* Fix spacing for task detail view in lists with a background
* Fix table headers wrapping in table view
* Fix table text alignment in task detail page
* Fix table view scrolling on mobile
* Fix test for saving a task description
* Fix tests failing on thursdays
* Fix token in storage not getting renewed
* Fix translating dates
* Fix usage of / in sass
* Fix user name and avatar alignment in navbar
* Fix users not removed from the list in settings when unshared
* Fix user test fixtures
* fix: vuex mutation violation from draggable (#674)
### Dependency Updates
* chore(deps): update dependency @4tw/cypress-drag-drop to v1.8.1 (#693)
* chore(deps): update dependency autoprefixer to v10.3.3 (#684)
* chore(deps): update dependency autoprefixer to v10.3.4 (#697)
* chore(deps): update dependency axios to v0.21.2 (#698)
* chore(deps): update dependency axios to v0.21.3 (#700)
* chore(deps): update dependency cypress to v8.3.1 (#689)
* chore(deps): update dependency esbuild to v0.12.23 (#683)
* chore(deps): update dependency esbuild to v0.12.24 (#688)
* chore(deps): update dependency esbuild to v0.12.25 (#696)
* chore(deps): update dependency eslint-plugin-vue to v7.17.0 (#686)
* chore(deps): update dependency jest to v27.1.0 (#687)
* chore(deps): update dependency sass to v1.38.1 (#679)
* chore(deps): update dependency sass to v1.38.2 (#690)
* chore(deps): update dependency sass to v1.39.0 (#695)
* chore(deps): update dependency typescript to v4.4.2 (#685)
* chore(deps): update dependency vite-plugin-pwa to v0.11.2 (#681)
* chore(deps): update dependency vite to v2.5.1 (#680)
* chore(deps): update dependency vite to v2.5.2 (#692)
* chore(deps): update dependency vite to v2.5.3 (#694)
* chore(deps): update typescript-eslint monorepo to v4.29.3 (#676)
* chore(deps): update typescript-eslint monorepo to v4.30.0 (#691)
* Update dependency autoprefixer to v10.3.2 (#670)
* Update dependency browserslist to v4.16.7 (#634)
* Update dependency browserslist to v4.16.8 (#664)
* Update dependency browserslist to v4.17.0 (#701)
* Update dependency bulma to v0.9.3 (#554)
* Update dependency cypress-file-upload to v5.0.8 (#556)
* Update dependency cypress to v7.3.0 (#507)
* Update dependency cypress to v7.4.0 (#517)
* Update dependency cypress to v7.5.0 (#541)
* Update dependency cypress to v7.6.0 (#561)
* Update dependency cypress to v7.7.0 (#577)
* Update dependency cypress to v8.1.0 (#624)
* Update dependency cypress to v8.2.0 (#637)
* Update dependency cypress to v8.3.0 (#660)
* Update dependency cypress to v8 (#601)
* Update dependency date-fns to v2.22.0 (#523)
* Update dependency date-fns to v2.22.1 (#524)
* Update dependency date-fns to v2.23.0 (#604)
* Update dependency dompurify to v2.2.9 (#529)
* Update dependency dompurify to v2.3.0 (#573)
* Update dependency dompurify to v2.3.1 (#655)
* Update dependency esbuild to v0.12.15 (#610)
* Update dependency esbuild to v0.12.16 (#614)
* Update dependency esbuild to v0.12.17 (#623)
* Update dependency esbuild to v0.12.18 (#638)
* Update dependency esbuild to v0.12.19 (#643)
* Update dependency esbuild to v0.12.20 (#654)
* Update dependency esbuild to v0.12.21 (#666)
* Update dependency esbuild to v0.12.22 (#668)
* Update dependency eslint-plugin-vue to v7.10.0 (#525)
* Update dependency eslint-plugin-vue to v7.11.0 (#547)
* Update dependency eslint-plugin-vue to v7.11.1 (#548)
* Update dependency eslint-plugin-vue to v7.12.1 (#565)
* Update dependency eslint-plugin-vue to v7.13.0 (#574)
* Update dependency eslint-plugin-vue to v7.14.0 (#597)
* Update dependency eslint-plugin-vue to v7.15.0 (#625)
* Update dependency eslint-plugin-vue to v7.15.1 (#633)
* Update dependency eslint-plugin-vue to v7.16.0 (#648)
* Update dependency eslint to v7.27.0 (#514)
* Update dependency eslint to v7.28.0 (#539)
* Update dependency eslint to v7.29.0 (#555)
* Update dependency eslint to v7.30.0 (#571)
* Update dependency eslint to v7.31.0 (#596)
* Update dependency eslint to v7.32.0 (#627)
* Update dependency highlight.js to v11.0.1 (#538)
* Update dependency highlight.js to v11.1.0 (#582)
* Update dependency highlight.js to v11.2.0 (#630)
* Update dependency highlight.js to v11 (#527)
* Update dependency jest to v27.0.3 (#526)
* Update dependency jest to v27.0.4 (#535)
* Update dependency jest to v27.0.5 (#558)
* Update dependency jest to v27.0.6 (#569)
* Update dependency jest to v27 (#519)
* Update dependency marked to v2.0.4 (#510)
* Update dependency marked to v2.0.5 (#513)
* Update dependency marked to v2.0.6 (#522)
* Update dependency marked to v2.0.7 (#532)
* Update dependency marked to v2.1.0 (#552)
* Update dependency marked to v2.1.1 (#553)
* Update dependency marked to v2.1.2 (#559)
* Update dependency marked to v2.1.3 (#567)
* Update dependency marked to v3 (#657)
* Update dependency @rollup/plugin-commonjs to v19.0.2 (#617)
* Update dependency sass to v1.33.0 (#512)
* Update dependency sass to v1.34.0 (#515)
* Update dependency sass to v1.34.1 (#534)
* Update dependency sass to v1.35.0 (#550)
* Update dependency sass to v1.35.1 (#551)
* Update dependency sass to v1.35.2 (#579)
* Update dependency sass to v1.36.0 (#606)
* Update dependency sass to v1.37.0 (#628)
* Update dependency sass to v1.37.2 (#632)
* Update dependency sass to v1.37.5 (#635)
* Update dependency sass to v1.38.0 (#661)
* Update dependency ts-jest to v27.0.4 (#602)
* Update dependency ts-jest to v27.0.5 (#662)
* Update dependency @types/jest to v27.0.1 (#653)
* Update dependency @types/jest to v27 (#650)
* Update dependency vite-plugin-pwa to v0.10.0 (#644)
* Update dependency vite-plugin-pwa to v0.11.0 (#667)
* Update dependency vite-plugin-pwa to v0.8.2 (#612)
* Update dependency vite-plugin-pwa to v0.9.3 (#629)
* Update dependency vite-plugin-vue2 to v1.7.3 (#613)
* Update dependency vite-plugin-vue2 to v1.8.0 (#646)
* Update dependency vite-plugin-vue2 to v1.8.1 (#656)
* Update dependency vite to v2.4.3 (#611)
* Update dependency vite to v2.4.4 (#619)
* Update dependency vite to v2.5.0 (#658)
* Update dependency vue-advanced-cropper to v1.6.0 (#516)
* Update dependency vue-advanced-cropper to v1.7.0 (#543)
* Update dependency vue-advanced-cropper to v1.8.0 (#641)
* Update dependency vue-advanced-cropper to v1.8.1 (#642)
* Update dependency vue-advanced-cropper to v1.8.2 (#645)
* Update dependency vue-flatpickr-component to v8.1.7 (#572)
* Update dependency vue-i18n to v8.24.5 (#564)
* Update dependency vue-i18n to v8.25.0 (#595)
* Update dependency vue-router to v3.5.2 (#557)
* Update dependency wait-on to v6 (#568)
* Update dependency workbox-cli to v6.1.5 (#609)
* Update Font Awesome (#636)
* Update Node.js (#549)
* Update Node.js to v16.4.1 (#576)
* Update Node.js to v16.4.2 (#578)
* Update typescript-eslint monorepo to v4.28.4 (#600)
* Update typescript-eslint monorepo to v4.28.5 (#618)
* Update typescript-eslint monorepo to v4.29.0 (#631)
* Update typescript-eslint monorepo to v4.29.1 (#647)
* Update typescript-eslint monorepo to v4.29.2 (#659)
* Update vue monorepo to v2.6.13 (#530)
* Update vue monorepo to v2.6.14 (#540)
* Update workbox monorepo to v6.2.0 (#639)
* Update workbox monorepo to v6.2.2 (#640)
* Update workbox monorepo to v6.2.4 (#649)
* User account deletion (#651)
* User Data Export and import (#699)
## [0.17.0 - 2021-05-14]
### Added
@ -489,8 +148,7 @@ The releases aim at the api versions which is why there are missing versions.
* Make sure all arm64 build steps run in parallel
* Make sure all empty pages have a call to action
* Make sure all popups & dropdowns are animated
* Make sure attachements are only added once to the list after uploading + Make sure the attachment list shows up every
time after adding an attachment
* Make sure attachements are only added once to the list after uploading + Make sure the attachment list shows up every time after adding an attachment
* Make sure no cta's are visible while the page is loading
* Make sure the loading spinner is always visible at the end of the page
* Make the button shadow lighter
@ -1017,7 +675,7 @@ The releases aim at the api versions which is why there are missing versions.
* Hide totp settings if it is disabled server side
* Increase network timeout when building docker image
* Make sure the version includes the tag when building docker images
* # PrideMonth
* #PrideMonth
* Only renew user token on tab focus events
* Redirect the user to login page if the token expired when the tab gets focus again
* Remove title length restrictions
@ -1052,7 +710,7 @@ The releases aim at the api versions which is why there are missing versions.
## [0.13] - 2020-05-12
#### Added
#### Added
* Add docker run script to change api url on startup
* Add github token for renovate (#89)
@ -1397,7 +1055,6 @@ The releases aim at the api versions which is why there are missing versions.
* Use email instead of username when resetting a password
### Fixed
* Fixed trying to verify an email when there was none
* Fixed loading tasks when the user was not authenticated

View File

@ -1,5 +1,5 @@
# Stage 1: Build application
FROM node:18 AS compile-image
FROM node:16 AS compile-image
WORKDIR /build
@ -24,6 +24,12 @@ RUN \
# Stage 2: copy
FROM nginx
RUN apt-get update && apt-get install -y apt-utils openssl && \
mkdir -p /etc/nginx/ssl && \
openssl genrsa -out /etc/nginx/ssl/dummy.key 2048 && \
openssl req -new -key /etc/nginx/ssl/dummy.key -out /etc/nginx/ssl/dummy.csr -subj "/C=DE/L=Berlin/O=Vikunja/CN=Vikunja Snakeoil" && \
openssl x509 -req -days 3650 -in /etc/nginx/ssl/dummy.csr -signkey /etc/nginx/ssl/dummy.key -out /etc/nginx/ssl/dummy.crt
COPY nginx.conf /etc/nginx/nginx.conf
COPY run.sh /run.sh

View File

@ -4,8 +4,8 @@
[![Build Status](https://drone.kolaente.de/api/badges/vikunja/frontend/status.svg)](https://drone.kolaente.de/vikunja/frontend)
[![License: AGPL v3](https://img.shields.io/badge/License-AGPL%20v3-blue.svg)](LICENSE)
[![Download](https://img.shields.io/badge/download-v0.18.2-brightgreen.svg)](https://dl.vikunja.io)
[![Translation](https://badges.crowdin.net/vikunja/localized.svg)](https://crowdin.com/project/vikunja)
[![Download](https://img.shields.io/badge/download-v0.17.0-brightgreen.svg)](https://dl.vikunja.io)
[![Translation](https://hosted.weblate.org/widgets/vikunja/-/frontend/svg-badge.svg)](https://hosted.weblate.org/engage/vikunja/)
This is the web frontend for Vikunja, written in Vue.js.
@ -20,25 +20,21 @@ If you find any security-related issues you don't want to disclose publicly, ple
There is a [docker image available](https://hub.docker.com/r/vikunja/api) with support for http/2 and aggressive caching enabled.
## Project setup
```shell
```
yarn install
```
### Compiles and hot-reloads for development
```shell
```
yarn run serve
```
### Compiles and minifies for production
```shell
```
yarn run build
```
### Lints and fixes files
```shell
```
yarn run lint
```

5
babel.config.js Normal file
View File

@ -0,0 +1,5 @@
module.exports = {
presets: [
'@vue/app'
]
}

View File

@ -1,12 +1,11 @@
{
"baseUrl": "http://localhost:4173",
"baseUrl": "http://localhost:8080",
"env": {
"API_URL": "http://localhost:3456/api/v1",
"TEST_SECRET": "averyLongSecretToSe33dtheDB"
"TEST_SECRET": "testingS3cr3et"
},
"video": false,
"retries": {
"runMode": 2
},
"projectId": "181c7x"
}
}

View File

@ -9,8 +9,8 @@
## Fixtures
We're using the [test endpoint](https://vikunja.io/docs/config-options/#testingtoken) of the vikunja api to
seed the database with test data before running the tests.
We're using the [test endpoint](https://vikunja.io/docs/config-options/#testingtoken) of the vikunja api to
seed the database with test data before running the tests.
This ensures better reproducability of tests.
## Running The Tests Locally
@ -22,20 +22,20 @@ It uses the same configuration as the CI.
To use it, run
```shell
```
docker-compose up -d
```
Then, once all containers are started, run
```shell
docker-compose run cypress bash
```
docker-composer run cypress bash
```
to get a shell inside the cypress container.
In that shell you can then execute the tests with
```shell
```
yarn test:frontend
```
@ -43,6 +43,6 @@ yarn test:frontend
To open the Cypress Dashboard and run tests from there, run
```shell
```
yarn cypress:open
```

View File

@ -2,12 +2,10 @@ version: '3'
services:
api:
image: vikunja/api:unstable
image: vikunja/api
environment:
VIKUNJA_LOG_LEVEL: DEBUG
VIKUNJA_SERVICE_TESTINGTOKEN: averyLongSecretToSe33dtheDB
ports:
- 3456:3456
cypress:
image: cypress/browsers:node12.18.3-chrome87-ff82
volumes:

View File

@ -1,4 +1,4 @@
import faker from '@faker-js/faker'
import faker from 'faker'
import {Factory} from '../support/factory'
import {formatISO} from 'date-fns'

View File

@ -1,4 +1,4 @@
import faker from '@faker-js/faker'
import faker from 'faker'
import {Factory} from '../support/factory'
import {formatISO} from 'date-fns'

View File

@ -1,6 +1,6 @@
import {Factory} from '../support/factory'
import {formatISO} from "date-fns"
import faker from '@faker-js/faker'
import faker from 'faker'
export class LinkShareFactory extends Factory {
static table = 'link_shares'

View File

@ -1,6 +1,6 @@
import {Factory} from '../support/factory'
import {formatISO} from "date-fns"
import faker from '@faker-js/faker'
import faker from 'faker'
export class ListFactory extends Factory {
static table = 'lists'

View File

@ -1,4 +1,4 @@
import faker from '@faker-js/faker'
import faker from 'faker'
import {Factory} from '../support/factory'
import {formatISO} from 'date-fns'

View File

@ -1,4 +1,4 @@
import faker from '@faker-js/faker'
import faker from 'faker'
import {Factory} from '../support/factory'
import {formatISO} from 'date-fns'
@ -14,6 +14,7 @@ export class TaskFactory extends Factory {
done: false,
list_id: 1,
created_by_id: 1,
is_favorite: false,
index: '{increment}',
created: formatISO(now),
updated: formatISO(now)

View File

@ -1,4 +1,4 @@
import faker from '@faker-js/faker'
import faker from 'faker'
import {Factory} from '../support/factory'
import {formatISO} from "date-fns"

View File

@ -1,4 +1,4 @@
import faker from '@faker-js/faker'
import faker from 'faker'
import {Factory} from '../support/factory'
import {formatISO} from 'date-fns'

View File

@ -1,4 +1,4 @@
import faker from '@faker-js/faker'
import faker from 'faker'
import {Factory} from '../support/factory'
import {formatISO} from "date-fns"
@ -11,9 +11,9 @@ export class UserFactory extends Factory {
return {
id: '{increment}',
username: faker.lorem.word(10) + faker.datatype.uuid(),
username: faker.lorem.word(10) + faker.random.uuid(),
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.', // 1234
status: 0,
is_active: true,
created: formatISO(now),
updated: formatISO(now)
}

View File

@ -1,56 +0,0 @@
import {ListFactory} from '../../factories/list'
import '../../support/authenticateUser'
import {prepareLists} from './prepareLists'
describe('List History', () => {
prepareLists()
it('should show a list history on the home page', () => {
cy.intercept(Cypress.env('API_URL') + '/namespaces*').as('loadNamespaces')
cy.intercept(Cypress.env('API_URL') + '/lists/*').as('loadList')
const lists = ListFactory.create(6)
cy.visit('/')
cy.wait('@loadNamespaces')
cy.get('body')
.should('not.contain', 'Last viewed')
cy.visit(`/lists/${lists[0].id}`)
cy.wait('@loadNamespaces')
cy.wait('@loadList')
cy.visit(`/lists/${lists[1].id}`)
cy.wait('@loadNamespaces')
cy.wait('@loadList')
cy.visit(`/lists/${lists[2].id}`)
cy.wait('@loadNamespaces')
cy.wait('@loadList')
cy.visit(`/lists/${lists[3].id}`)
cy.wait('@loadNamespaces')
cy.wait('@loadList')
cy.visit(`/lists/${lists[4].id}`)
cy.wait('@loadNamespaces')
cy.wait('@loadList')
cy.visit(`/lists/${lists[5].id}`)
cy.wait('@loadNamespaces')
cy.wait('@loadList')
// cy.visit('/')
// cy.wait('@loadNamespaces')
// Not using cy.visit here to work around the redirect issue fixed in #1337
cy.get('nav.menu.top-menu a')
.contains('Overview')
.click()
cy.get('body')
.should('contain', 'Last viewed')
cy.get('.list-cards-wrapper-2-rows')
.should('not.contain', lists[0].title)
.should('contain', lists[1].title)
.should('contain', lists[2].title)
.should('contain', lists[3].title)
.should('contain', lists[4].title)
.should('contain', lists[5].title)
})
})

View File

@ -1,76 +0,0 @@
import {formatISO, format} from 'date-fns'
import {TaskFactory} from '../../factories/task'
import {prepareLists} from './prepareLists'
import '../../support/authenticateUser'
describe('List View Gantt', () => {
prepareLists()
it('Hides tasks with no dates', () => {
const tasks = TaskFactory.create(1)
cy.visit('/lists/1/gantt')
cy.get('.gantt-chart .tasks')
.should('not.contain', tasks[0].title)
})
it('Shows tasks from the current and next month', () => {
const now = new Date()
const nextMonth = now
nextMonth.setDate(1)
nextMonth.setMonth(now.getMonth() + 1)
cy.visit('/lists/1/gantt')
cy.get('.gantt-chart .months')
.should('contain', format(now, 'MMMM'))
.should('contain', format(nextMonth, 'MMMM'))
})
it('Shows tasks with dates', () => {
const now = new Date()
const tasks = TaskFactory.create(1, {
start_date: formatISO(now),
end_date: formatISO(now.setDate(now.getDate() + 4))
})
cy.visit('/lists/1/gantt')
cy.get('.gantt-chart .tasks')
.should('not.be.empty')
cy.get('.gantt-chart .tasks')
.should('contain', tasks[0].title)
})
it('Shows tasks with no dates after enabling them', () => {
TaskFactory.create(1, {
start_date: null,
end_date: null,
})
cy.visit('/lists/1/gantt')
cy.get('.gantt-options .fancycheckbox')
.contains('Show tasks which don\'t have dates set')
.click()
cy.get('.gantt-chart .tasks')
.should('not.be.empty')
cy.get('.gantt-chart .tasks .task.nodate')
.should('exist')
})
it('Drags a task around', () => {
const now = new Date()
TaskFactory.create(1, {
start_date: formatISO(now),
end_date: formatISO(now.setDate(now.getDate() + 4))
})
cy.visit('/lists/1/gantt')
cy.get('.gantt-chart .tasks .task')
.first()
.trigger('mousedown', {which: 1})
.trigger('mousemove', {clientX: 500, clientY: 0})
.trigger('mouseup', {force: true})
})
})

View File

@ -1,196 +0,0 @@
import {BucketFactory} from '../../factories/bucket'
import {ListFactory} from '../../factories/list'
import {TaskFactory} from '../../factories/task'
import {prepareLists} from './prepareLists'
import '../../support/authenticateUser'
describe('List View Kanban', () => {
let buckets
prepareLists()
beforeEach(() => {
buckets = BucketFactory.create(2)
})
it('Shows all buckets with their tasks', () => {
const data = TaskFactory.create(10, {
list_id: 1,
bucket_id: 1,
})
cy.visit('/lists/1/kanban')
cy.get('.kanban .bucket .title')
.contains(buckets[0].title)
.should('exist')
cy.get('.kanban .bucket .title')
.contains(buckets[1].title)
.should('exist')
cy.get('.kanban .bucket')
.first()
.should('contain', data[0].title)
})
it('Can add a new task to a bucket', () => {
TaskFactory.create(2, {
list_id: 1,
bucket_id: 1,
})
cy.visit('/lists/1/kanban')
cy.getSettled('.kanban .bucket')
.contains(buckets[0].title)
.get('.bucket-footer .button')
.contains('Add another task')
.click()
cy.get('.kanban .bucket')
.contains(buckets[0].title)
.get('.bucket-footer .field .control input.input')
.type('New Task{enter}')
cy.get('.kanban .bucket')
.first()
.should('contain', 'New Task')
})
it('Can create a new bucket', () => {
cy.visit('/lists/1/kanban')
cy.get('.kanban .bucket.new-bucket .button')
.click()
cy.get('.kanban .bucket.new-bucket input.input')
.type('New Bucket{enter}')
cy.wait(1000) // Wait for the request to finish
cy.get('.kanban .bucket .title')
.contains('New Bucket')
.should('exist')
})
it('Can set a bucket limit', () => {
cy.visit('/lists/1/kanban')
cy.getSettled('.kanban .bucket .bucket-header .dropdown.options .dropdown-trigger')
.first()
.click()
cy.get('.kanban .bucket .bucket-header .dropdown.options .dropdown-menu .dropdown-item')
.contains('Limit: Not Set')
.click()
cy.get('.kanban .bucket .bucket-header .dropdown.options .dropdown-menu .dropdown-item .field input.input')
.first()
.type(3)
cy.get('[data-cy="setBucketLimit"]')
.first()
.click()
cy.get('.kanban .bucket .bucket-header span.limit')
.contains('0/3')
.should('exist')
})
it('Can rename a bucket', () => {
cy.visit('/lists/1/kanban')
cy.getSettled('.kanban .bucket .bucket-header .title')
.first()
.type('{selectall}New Bucket Title{enter}')
cy.get('.kanban .bucket .bucket-header .title')
.first()
.should('contain', 'New Bucket Title')
})
it('Can delete a bucket', () => {
cy.visit('/lists/1/kanban')
cy.getSettled('.kanban .bucket .bucket-header .dropdown.options .dropdown-trigger')
.first()
.click()
cy.get('.kanban .bucket .bucket-header .dropdown.options .dropdown-menu .dropdown-item')
.contains('Delete')
.click()
cy.get('.modal-mask .modal-container .modal-content .header')
.should('contain', 'Delete the bucket')
cy.get('.modal-mask .modal-container .modal-content .actions .button')
.contains('Do it!')
.click()
cy.get('.kanban .bucket .title')
.contains(buckets[0].title)
.should('not.exist')
cy.get('.kanban .bucket .title')
.contains(buckets[1].title)
.should('exist')
})
it('Can drag tasks around', () => {
const tasks = TaskFactory.create(2, {
list_id: 1,
bucket_id: 1,
})
cy.visit('/lists/1/kanban')
cy.getSettled('.kanban .bucket .tasks .task')
.contains(tasks[0].title)
.first()
.drag('.kanban .bucket:nth-child(2) .tasks')
cy.get('.kanban .bucket:nth-child(2) .tasks')
.should('contain', tasks[0].title)
cy.get('.kanban .bucket:nth-child(1) .tasks')
.should('not.contain', tasks[0].title)
})
it('Should navigate to the task when the task card is clicked', () => {
const tasks = TaskFactory.create(5, {
id: '{increment}',
list_id: 1,
bucket_id: 1,
})
cy.visit('/lists/1/kanban')
cy.getSettled('.kanban .bucket .tasks .task')
.contains(tasks[0].title)
.should('be.visible')
.click()
cy.url()
.should('contain', `/tasks/${tasks[0].id}`, { timeout: 1000 })
})
it('Should remove a task from the kanban board when moving it to another list', () => {
const lists = ListFactory.create(2)
BucketFactory.create(2, {
list_id: '{increment}',
})
const tasks = TaskFactory.create(5, {
id: '{increment}',
list_id: 1,
bucket_id: 1,
})
const task = tasks[0]
cy.visit('/lists/1/kanban')
cy.getSettled('.kanban .bucket .tasks .task')
.contains(task.title)
.should('be.visible')
.click()
cy.get('.task-view .action-buttons .button', { timeout: 3000 })
.contains('Move')
.click()
cy.get('.task-view .content.details .field .multiselect.control .input-wrapper input')
.type(`${lists[1].title}{enter}`)
// The requests happen with a 200ms timeout. Because of that, the results are not yet there when cypress
// presses enter and we can't simulate pressing on enter to select the item.
cy.get('.task-view .content.details .field .multiselect.control .search-results')
.children()
.first()
.click()
cy.get('.global-notification', { timeout: 1000 })
.should('contain', 'Success')
cy.go('back')
cy.get('.kanban .bucket')
.should('not.contain', task.title)
})
})

View File

@ -1,97 +0,0 @@
import {UserListFactory} from '../../factories/users_list'
import {TaskFactory} from '../../factories/task'
import {UserFactory} from '../../factories/user'
import {ListFactory} from '../../factories/list'
import {prepareLists} from './prepareLists'
import '../../support/authenticateUser'
describe('List View List', () => {
prepareLists()
it('Should be an empty list', () => {
cy.visit('/lists/1')
cy.url()
.should('contain', '/lists/1/list')
cy.get('.list-title h1')
.should('contain', 'First List')
cy.get('.list-title .dropdown')
.should('exist')
cy.get('p')
.contains('This list is currently empty.')
.should('exist')
})
it('Should navigate to the task when the title is clicked', () => {
const tasks = TaskFactory.create(5, {
id: '{increment}',
list_id: 1,
})
cy.visit('/lists/1/list')
cy.get('.tasks .task .tasktext')
.contains(tasks[0].title)
.first()
.click()
cy.url()
.should('contain', `/tasks/${tasks[0].id}`)
})
it('Should not see any elements for a list which is shared read only', () => {
UserFactory.create(2)
UserListFactory.create(1, {
list_id: 2,
user_id: 1,
right: 0,
})
const lists = ListFactory.create(2, {
owner_id: '{increment}',
namespace_id: '{increment}',
})
cy.visit(`/lists/${lists[1].id}/`)
cy.get('.list-title a.icon')
.should('not.exist')
cy.get('input.input[placeholder="Add a new task..."')
.should('not.exist')
})
it('Should only show the color of a list in the navigation and not in the list view', () => {
const lists = ListFactory.create(1, {
hex_color: '00db60',
})
TaskFactory.create(10, {
list_id: lists[0].id,
})
cy.visit(`/lists/${lists[0].id}/`)
cy.get('.menu-list li .list-menu-link .color-bubble')
.should('have.css', 'background-color', 'rgb(0, 219, 96)')
cy.get('.tasks-container .tasks .color-bubble')
.should('not.exist')
})
it('Should paginate for > 50 tasks', () => {
const tasks = TaskFactory.create(100, {
id: '{increment}',
title: i => `task${i}`,
list_id: 1,
})
cy.visit('/lists/1/list')
cy.get('.tasks-container .tasks')
.should('contain', tasks[99].title)
cy.get('.card-content .pagination .pagination-link')
.contains('2')
.click()
cy.url()
.should('contain', '?page=2')
cy.get('.tasks-container .tasks')
.should('contain', tasks[1].title)
cy.get('.tasks-container .tasks')
.should('not.contain', tasks[99].title)
})
})

View File

@ -1,52 +0,0 @@
import {TaskFactory} from '../../factories/task'
import '../../support/authenticateUser'
describe('List View Table', () => {
it('Should show a table with tasks', () => {
const tasks = TaskFactory.create(1)
cy.visit('/lists/1/table')
cy.get('.list-table table.table')
.should('exist')
cy.get('.list-table table.table')
.should('contain', tasks[0].title)
})
it('Should have working column switches', () => {
TaskFactory.create(1)
cy.visit('/lists/1/table')
cy.get('.list-table .filter-container .items .button')
.contains('Columns')
.click()
cy.get('.list-table .filter-container .card.columns-filter .card-content .fancycheckbox .check')
.contains('Priority')
.click()
cy.get('.list-table .filter-container .card.columns-filter .card-content .fancycheckbox .check')
.contains('Done')
.click()
cy.get('.list-table table.table th')
.contains('Priority')
.should('exist')
cy.get('.list-table table.table th')
.contains('Done')
.should('not.exist')
})
it('Should navigate to the task when the title is clicked', () => {
const tasks = TaskFactory.create(5, {
id: '{increment}',
list_id: 1,
})
cy.visit('/lists/1/table')
cy.get('.list-table table.table')
.contains(tasks[0].title)
.click()
cy.url()
.should('contain', `/tasks/${tasks[0].id}`)
})
})

View File

@ -1,11 +1,25 @@
import {formatISO, format} from 'date-fns'
import {TaskFactory} from '../../factories/task'
import {prepareLists} from './prepareLists'
import {ListFactory} from '../../factories/list'
import {UserListFactory} from '../../factories/users_list'
import {UserFactory} from '../../factories/user'
import {NamespaceFactory} from '../../factories/namespace'
import {BucketFactory} from '../../factories/bucket'
import '../../support/authenticateUser'
describe('Lists', () => {
let lists
prepareLists((newLists) => (lists = newLists))
beforeEach(() => {
UserFactory.create(1)
NamespaceFactory.create(1)
lists = ListFactory.create(1, {
title: 'First List'
})
TaskFactory.truncate()
})
it('Should create a new list', () => {
cy.visit('/')
@ -15,16 +29,17 @@ describe('Lists', () => {
.contains('New list')
.click()
cy.url()
.should('contain', '/lists/new/1')
.should('contain', '/namespaces/1/list')
cy.get('.card-header-title')
.contains('New list')
.contains('Create a new list')
cy.get('input.input')
.type('New List')
cy.get('.button')
.contains('Create')
.click()
cy.get('.global-notification', { timeout: 1000 }) // Waiting until the request to create the new list is done
cy.wait(1000) // Waiting until the request to create the new list is done
cy.get('.global-notification')
.should('contain', 'Success')
cy.url()
.should('contain', '/lists/')
@ -42,7 +57,7 @@ describe('Lists', () => {
})
it('Should rename the list in all places', () => {
TaskFactory.create(5, {
const tasks = TaskFactory.create(5, {
id: '{increment}',
list_id: 1,
})
@ -57,7 +72,7 @@ describe('Lists', () => {
cy.get('.namespace-container .menu.namespaces-lists .more-container .menu-list li:first-child .dropdown .dropdown-content')
.contains('Edit')
.click()
cy.get('#title')
cy.get('#listtext')
.type(`{selectall}${newListName}`)
cy.get('footer.modal-card-foot .button')
.contains('Save')
@ -72,7 +87,7 @@ describe('Lists', () => {
.should('contain', newListName)
.should('not.contain', lists[0].title)
cy.visit('/')
cy.get('.card-content')
cy.get('.card-content .tasks')
.should('contain', newListName)
.should('not.contain', lists[0].title)
})
@ -87,7 +102,7 @@ describe('Lists', () => {
.click()
cy.url()
.should('contain', '/settings/delete')
cy.get('[data-cy="modalPrimary"]')
cy.get('.modal-mask .modal-container .modal-content .actions a.button')
.contains('Do it')
.click()
@ -98,4 +113,432 @@ describe('Lists', () => {
cy.location('pathname')
.should('equal', '/')
})
describe('List View', () => {
it('Should be an empty list', () => {
cy.visit('/lists/1')
cy.url()
.should('contain', '/lists/1/list')
cy.get('.list-title h1')
.should('contain', 'First List')
cy.get('.list-title .dropdown')
.should('exist')
cy.get('p')
.contains('This list is currently empty.')
.should('exist')
})
it('Should navigate to the task when the title is clicked', () => {
const tasks = TaskFactory.create(5, {
id: '{increment}',
list_id: 1,
})
cy.visit('/lists/1/list')
cy.get('.tasks .task .tasktext')
.contains(tasks[0].title)
.first()
.click()
cy.url()
.should('contain', `/tasks/${tasks[0].id}`)
})
it('Should not see any elements for a list which is shared read only', () => {
UserFactory.create(2)
UserListFactory.create(1, {
list_id: 2,
user_id: 1,
right: 0,
})
const lists = ListFactory.create(2, {
owner_id: '{increment}',
namespace_id: '{increment}',
})
cy.visit(`/lists/${lists[1].id}/`)
cy.get('.list-title a.icon')
.should('not.exist')
cy.get('input.input[placeholder="Add a new task..."')
.should('not.exist')
})
it('Should only show the color of a list in the navigation and not in the list view', () => {
const lists = ListFactory.create(1, {
hex_color: '00db60',
})
TaskFactory.create(10, {
list_id: lists[0].id,
})
cy.visit(`/lists/${lists[0].id}/`)
cy.get('.menu-list li .list-menu-link .color-bubble')
.should('have.css', 'background-color', 'rgb(0, 219, 96)')
cy.get('.tasks-container .tasks .color-bubble')
.should('not.exist')
})
it('Should paginate for > 50 tasks', () => {
const tasks = TaskFactory.create(100, {
id: '{increment}',
title: i => `task${i}`,
list_id: 1,
})
cy.visit('/lists/1/list')
cy.get('.tasks-container .tasks')
.should('contain', tasks[99].title)
cy.get('.card-content .pagination .pagination-link')
.contains('2')
.click()
cy.url()
.should('contain', '?page=2')
cy.get('.tasks-container .tasks')
.should('contain', tasks[1].title)
cy.get('.tasks-container .tasks')
.should('not.contain', tasks[99].title)
})
})
describe('Table View', () => {
it('Should show a table with tasks', () => {
const tasks = TaskFactory.create(1)
cy.visit('/lists/1/table')
cy.get('.table-view table.table')
.should('exist')
cy.get('.table-view table.table')
.should('contain', tasks[0].title)
})
it('Should have working column switches', () => {
TaskFactory.create(1)
cy.visit('/lists/1/table')
cy.get('.table-view .filter-container .items .button')
.contains('Columns')
.click()
cy.get('.table-view .filter-container .card .card-content .fancycheckbox .check')
.contains('Priority')
.click()
cy.get('.table-view .filter-container .card .card-content .fancycheckbox .check')
.contains('Done')
.click()
cy.get('.table-view table.table th')
.contains('Priority')
.should('exist')
cy.get('.table-view table.table th')
.contains('Done')
.should('not.exist')
})
it('Should navigate to the task when the title is clicked', () => {
const tasks = TaskFactory.create(5, {
id: '{increment}',
list_id: 1,
})
cy.visit('/lists/1/table')
cy.get('.table-view table.table')
.contains(tasks[0].title)
.click()
cy.url()
.should('contain', `/tasks/${tasks[0].id}`)
})
})
describe('Gantt View', () => {
it('Hides tasks with no dates', () => {
TaskFactory.create(1)
cy.visit('/lists/1/gantt')
cy.get('.gantt-chart-container .gantt-chart .tasks')
.should('be.empty')
})
it('Shows tasks from the current and next month', () => {
const now = new Date()
const nextMonth = now
nextMonth.setDate(1)
nextMonth.setMonth(now.getMonth() + 1)
cy.visit('/lists/1/gantt')
cy.get('.gantt-chart-container .gantt-chart .months')
.should('contain', format(now, 'MMMM'))
.should('contain', format(nextMonth, 'MMMM'))
})
it('Shows tasks with dates', () => {
const now = new Date()
const tasks = TaskFactory.create(1, {
start_date: formatISO(now),
end_date: formatISO(now.setDate(now.getDate() + 4))
})
cy.visit('/lists/1/gantt')
cy.get('.gantt-chart-container .gantt-chart .tasks')
.should('not.be.empty')
cy.get('.gantt-chart-container .gantt-chart .tasks')
.should('contain', tasks[0].title)
})
it('Shows tasks with no dates after enabling them', () => {
TaskFactory.create(1, {
start_date: null,
end_date: null,
})
cy.visit('/lists/1/gantt')
cy.get('.gantt-chart-container .gantt-options .fancycheckbox')
.contains('Show tasks which don\'t have dates set')
.click()
cy.get('.gantt-chart-container .gantt-chart .tasks')
.should('not.be.empty')
cy.get('.gantt-chart-container .gantt-chart .tasks .task.nodate')
.should('exist')
})
it('Drags a task around', () => {
const now = new Date()
TaskFactory.create(1, {
start_date: formatISO(now),
end_date: formatISO(now.setDate(now.getDate() + 4))
})
cy.visit('/lists/1/gantt')
cy.get('.gantt-chart-container .gantt-chart .tasks .task')
.first()
.trigger('mousedown', {which: 1})
.trigger('mousemove', {clientX: 500, clientY: 0})
.trigger('mouseup', {force: true})
})
})
describe('Kanban', () => {
let buckets
beforeEach(() => {
buckets = BucketFactory.create(2)
})
it('Shows all buckets with their tasks', () => {
const data = TaskFactory.create(10, {
list_id: 1,
bucket_id: 1,
})
cy.visit('/lists/1/kanban')
cy.get('.kanban .bucket .title')
.contains(buckets[0].title)
.should('exist')
cy.get('.kanban .bucket .title')
.contains(buckets[1].title)
.should('exist')
cy.get('.kanban .bucket')
.first()
.should('contain', data[0].title)
})
it('Can add a new task to a bucket', () => {
const data = TaskFactory.create(2, {
list_id: 1,
bucket_id: 1,
})
cy.visit('/lists/1/kanban')
cy.get('.kanban .bucket')
.contains(buckets[0].title)
.get('.bucket-footer .button')
.contains('Add another task')
.click()
cy.get('.kanban .bucket')
.contains(buckets[0].title)
.get('.bucket-footer .field .control input.input')
.type('New Task{enter}')
cy.get('.kanban .bucket')
.first()
.should('contain', 'New Task')
})
it('Can create a new bucket', () => {
cy.visit('/lists/1/kanban')
cy.get('.kanban .bucket.new-bucket .button')
.click()
cy.get('.kanban .bucket.new-bucket input.input')
.type('New Bucket{enter}')
cy.wait(1000) // Wait for the request to finish
cy.get('.kanban .bucket .title')
.contains('New Bucket')
.should('exist')
})
it('Can set a bucket limit', () => {
cy.visit('/lists/1/kanban')
cy.get('.kanban .bucket .bucket-header .dropdown.options .dropdown-trigger')
.first()
.click()
cy.get('.kanban .bucket .bucket-header .dropdown.options .dropdown-menu .dropdown-item')
.contains('Limit: Not Set')
.click()
cy.get('.kanban .bucket .bucket-header .dropdown.options .dropdown-menu .dropdown-item .field input.input')
.first()
.type(3)
cy.get('.kanban .bucket .bucket-header .dropdown.options .dropdown-menu .dropdown-item .field a.button.is-primary')
.first()
.click()
cy.get('.kanban .bucket .bucket-header span.limit')
.contains('0/3')
.should('exist')
})
it('Can rename a bucket', () => {
cy.visit('/lists/1/kanban')
cy.get('.kanban .bucket .bucket-header .title')
.first()
.type('{selectall}New Bucket Title{enter}')
cy.get('.kanban .bucket .bucket-header .title')
.first()
.should('contain', 'New Bucket Title')
})
it('Can delete a bucket', () => {
cy.visit('/lists/1/kanban')
cy.get('.kanban .bucket .bucket-header .dropdown.options .dropdown-trigger')
.first()
.click()
cy.get('.kanban .bucket .bucket-header .dropdown.options .dropdown-menu .dropdown-item')
.contains('Delete')
.click()
cy.get('.modal-mask .modal-container .modal-content .header')
.should('contain', 'Delete the bucket')
cy.get('.modal-mask .modal-container .modal-content .actions .button')
.contains('Do it!')
.click()
cy.get('.kanban .bucket .title')
.contains(buckets[0].title)
.should('not.exist')
cy.get('.kanban .bucket .title')
.contains(buckets[1].title)
.should('exist')
})
// The following test does not work. It seems like vue-smooth-dnd does not use either mousemove or dragstart
// (not sure why this actually works at all?) and as I'm planning to swap that out for vuedraggable/sortable.js
// anyway, I figured it wouldn't be worth the hassle right now.
// it('Can drag tasks around', () => {
// const tasks = TaskFactory.create(2, {
// list_id: 1,
// bucket_id: 1,
// })
// cy.visit('/lists/1/kanban')
//
// cy.get('.kanban .bucket .tasks .task')
// .contains(tasks[0].title)
// .first()
// .drag('.kanban .bucket:nth-child(2) .tasks .smooth-dnd-container.vertical')
// .trigger('mousedown', {which: 1})
// .trigger('mousemove', {clientX: 500, clientY: 0})
// .trigger('mouseup', {force: true})
// })
it('Should navigate to the task when the task card is clicked', () => {
const tasks = TaskFactory.create(5, {
id: '{increment}',
list_id: 1,
bucket_id: 1,
})
cy.visit('/lists/1/kanban')
cy.getSettled('.kanban .bucket .tasks .task')
.contains(tasks[0].title)
.should('be.visible')
.click()
cy.url()
.should('contain', `/tasks/${tasks[0].id}`)
})
it('Should remove a task from the kanban board when moving it to another list', () => {
const lists = ListFactory.create(2)
BucketFactory.create(2, {
list_id: '{increment}',
})
const tasks = TaskFactory.create(5, {
id: '{increment}',
list_id: 1,
bucket_id: 1,
})
const task = tasks[0]
cy.visit('/lists/1/kanban')
cy.getSettled('.kanban .bucket .tasks .task')
.contains(task.title)
.should('be.visible')
.click()
cy.get('.task-view .action-buttons .button')
.contains('Move task')
.click()
cy.get('.task-view .content.details .field .multiselect.control .input-wrapper input')
.type(`${lists[1].title}{enter}`)
// The requests happen with a 200ms timeout. Because of that, the results are not yet there when cypress
// presses enter and we can't simulate pressing on enter to select the item.
cy.get('.task-view .content.details .field .multiselect.control .search-results')
.children()
.first()
.click()
cy.get('.global-notification')
.should('contain', 'Success')
cy.go('back')
cy.get('.kanban .bucket')
.should('not.contain', task.title)
})
})
describe('List history', () => {
it('should show a list history on the home page', () => {
const lists = ListFactory.create(6)
cy.visit('/')
cy.get('h3')
.contains('Last viewed')
.should('not.exist')
cy.visit(`/lists/${lists[0].id}`)
cy.visit(`/lists/${lists[1].id}`)
cy.visit(`/lists/${lists[2].id}`)
cy.visit(`/lists/${lists[3].id}`)
cy.visit(`/lists/${lists[4].id}`)
cy.visit(`/lists/${lists[5].id}`)
cy.visit('/')
cy.get('h3')
.contains('Last viewed')
.should('exist')
cy.get('.list-cards-wrapper-2-rows')
.should('not.contain', lists[0].title)
.should('contain', lists[1].title)
.should('contain', lists[2].title)
.should('contain', lists[3].title)
.should('contain', lists[4].title)
.should('contain', lists[5].title)
})
})
})

View File

@ -15,7 +15,7 @@ describe('Namepaces', () => {
it('Should be all there', () => {
cy.visit('/namespaces')
cy.get('[data-cy="namespace-title"]')
cy.get('.namespace h1 span')
.should('contain', namespaces[0].title)
})
@ -23,14 +23,14 @@ describe('Namepaces', () => {
const newNamespaceTitle = 'New Namespace'
cy.visit('/namespaces')
cy.get('[data-cy="new-namespace"]')
.should('contain', 'New namespace')
cy.get('a.button')
.contains('Create a new namespace')
.click()
cy.url()
.should('contain', '/namespaces/new')
cy.get('.card-header-title')
.should('contain', 'New namespace')
.should('contain', 'Create a new namespace')
cy.get('input.input')
.type(newNamespaceTitle)
cy.get('.button')
@ -67,12 +67,12 @@ describe('Namepaces', () => {
.contains('Save')
.click()
cy.get('.global-notification', { timeout: 1000 })
cy.get('.global-notification')
.should('contain', 'Success')
cy.get('.namespace-container .menu.namespaces-lists')
.should('contain', newNamespaceName)
.should('not.contain', newNamespaces[0].title)
cy.get('[data-cy="namespaces-list"]')
cy.get('.content.namespaces-list')
.should('contain', newNamespaceName)
.should('not.contain', newNamespaces[0].title)
})
@ -89,7 +89,7 @@ describe('Namepaces', () => {
.click()
cy.url()
.should('contain', '/settings/delete')
cy.get('[data-cy="modalPrimary"]')
cy.get('.modal-mask .modal-container .modal-content .actions a.button')
.contains('Do it')
.click()
@ -116,30 +116,30 @@ describe('Namepaces', () => {
// Initial
cy.visit('/namespaces')
cy.get('.namespace')
cy.get('.namespaces-list .namespace')
.should('not.contain', 'Archived')
// Show archived
cy.get('[data-cy="show-archived-check"] label.check span')
cy.get('.namespaces-list .fancycheckbox.show-archived-check label.check span')
.should('be.visible')
.click()
cy.get('[data-cy="show-archived-check"] input')
cy.get('.namespaces-list .fancycheckbox.show-archived-check input')
.should('be.checked')
cy.get('.namespace')
cy.get('.namespaces-list .namespace')
.should('contain', 'Archived')
// Don't show archived
cy.get('[data-cy="show-archived-check"] label.check span')
cy.get('.namespaces-list .fancycheckbox.show-archived-check label.check span')
.should('be.visible')
.click()
cy.get('[data-cy="show-archived-check"] input')
cy.get('.namespaces-list .fancycheckbox.show-archived-check input')
.should('not.be.checked')
// Second time visiting after unchecking
cy.visit('/namespaces')
cy.get('[data-cy="show-archived-check"] input')
cy.get('.namespaces-list .fancycheckbox.show-archived-check input')
.should('not.be.checked')
cy.get('.namespace')
cy.get('.namespaces-list .namespace')
.should('not.contain', 'Archived')
})
})

View File

@ -1,16 +0,0 @@
import {ListFactory} from '../../factories/list'
import {UserFactory} from '../../factories/user'
import {NamespaceFactory} from '../../factories/namespace'
import {TaskFactory} from '../../factories/task'
export function prepareLists(setLists = () => {}) {
beforeEach(() => {
UserFactory.create(1)
NamespaceFactory.create(1)
const lists = ListFactory.create(1, {
title: 'First List'
})
setLists(lists)
TaskFactory.truncate()
})
}

View File

@ -0,0 +1,35 @@
import '../../support/authenticateUser'
const setHours = hours => {
const date = new Date()
date.setHours(hours)
cy.clock(+date)
}
describe('Home Page', () => {
it('shows the right salutation in the night', () => {
setHours(4)
cy.visit('/')
cy.get('h2').should('contain', 'Good Night')
})
it('shows the right salutation in the morning', () => {
setHours(8)
cy.visit('/')
cy.get('h2').should('contain', 'Good Morning')
})
it('shows the right salutation in the day', () => {
setHours(13)
cy.visit('/')
cy.get('h2').should('contain', 'Hi')
})
it('shows the right salutation in the night', () => {
setHours(20)
cy.visit('/')
cy.get('h2').should('contain', 'Good Evening')
})
it('shows the right salutation in the night again', () => {
setHours(23)
cy.visit('/')
cy.get('h2').should('contain', 'Good Night')
})
})

View File

@ -7,7 +7,7 @@ describe('The Menu', () => {
})
it('Can be hidden on desktop', () => {
cy.get('button.menu-show-button:visible')
cy.get('a.menu-show-button:visible')
.click()
cy.get('.namespace-container')
.should('not.have.class', 'is-active')
@ -21,7 +21,7 @@ describe('The Menu', () => {
it('Is can be shown on mobile', () => {
cy.viewport('iphone-8')
cy.get('button.menu-show-button:visible')
cy.get('a.menu-show-button:visible')
.click()
cy.get('.namespace-container')
.should('have.class', 'is-active')

View File

@ -27,7 +27,7 @@ describe('Task', () => {
it('Should be created new', () => {
cy.visit('/lists/1/list')
cy.get('.input[placeholder="Add a new task…"')
cy.get('input.input[placeholder="Add a new task…"')
.type('New Task')
cy.get('.button')
.contains('Add')
@ -43,7 +43,7 @@ describe('Task', () => {
cy.visit('/lists/1/list')
cy.get('.list-is-empty-notice')
.should('not.exist')
cy.get('.input[placeholder="Add a new task…"')
cy.get('input.input[placeholder="Add a new task…"')
.type('New Task')
cy.get('.button')
.contains('Add')
@ -116,7 +116,6 @@ describe('Task', () => {
.should('be.visible')
.should('contain', 'Done')
cy.get('.task-view .action-buttons p.created')
.scrollIntoView()
.should('be.visible')
.should('contain', 'Done')
})
@ -129,7 +128,7 @@ describe('Task', () => {
cy.visit(`/tasks/${tasks[0].id}`)
cy.get('.task-view .action-buttons .button')
.contains('Mark task done!')
.contains('Done!')
.click()
cy.get('.task-view .heading .is-done')
@ -169,8 +168,8 @@ describe('Task', () => {
.click()
cy.get('.task-view .details.content.description .editor .vue-easymde .EasyMDEContainer .CodeMirror-scroll')
.type('{selectall}New Description')
cy.get('[data-cy="saveEditor"]')
.contains('Save')
cy.get('.task-view .details.content.description .editor a')
.contains('Done')
.click()
cy.get('.task-view .details.content.description h3 span.is-small.has-text-success')
@ -210,7 +209,7 @@ describe('Task', () => {
cy.visit(`/tasks/${tasks[0].id}`)
cy.get('.task-view .action-buttons .button')
.contains('Move')
.contains('Move task')
.click()
cy.get('.task-view .content.details .field .multiselect.control .input-wrapper input')
.type(`${lists[1].title}{enter}`)
@ -237,7 +236,7 @@ describe('Task', () => {
cy.get('.task-view .action-buttons .button')
.should('be.visible')
.contains('Delete')
.contains('Delete task')
.click()
cy.get('.modal-mask .modal-container .modal-content .header')
.should('contain', 'Delete this task')
@ -264,7 +263,8 @@ describe('Task', () => {
cy.visit(`/tasks/${tasks[0].id}`)
cy.get('[data-cy="taskDetail.assign"]')
cy.get('.task-view .action-buttons .button')
.contains('Assign this task to a user')
.click()
cy.get('.task-view .column.assignees .multiselect input')
.type(users[1].username)
@ -317,7 +317,7 @@ describe('Task', () => {
cy.visit(`/tasks/${tasks[0].id}`)
cy.get('.task-view .action-buttons .button')
.contains('Add Labels')
.contains('Add labels')
.should('be.visible')
.click()
cy.get('.task-view .details.labels-list .multiselect input')
@ -344,7 +344,7 @@ describe('Task', () => {
cy.visit(`/tasks/${tasks[0].id}`)
cy.get('.task-view .action-buttons .button')
.contains('Add Labels')
.contains('Add labels')
.click()
cy.get('.task-view .details.labels-list .multiselect input')
.type(labels[0].title)
@ -353,7 +353,7 @@ describe('Task', () => {
.first()
.click()
cy.get('.global-notification', { timeout: 4000 })
cy.get('.global-notification')
.should('contain', 'Success')
cy.get('.task-view .details.labels-list .multiselect .input-wrapper span.tag')
.should('exist')
@ -373,13 +373,13 @@ describe('Task', () => {
cy.visit(`/tasks/${tasks[0].id}`)
cy.getSettled('.task-view .details.labels-list .multiselect .input-wrapper')
cy.get('.task-view .details.labels-list .multiselect .input-wrapper')
.should('be.visible')
.should('contain', labels[0].title)
cy.getSettled('.task-view .details.labels-list .multiselect .input-wrapper')
cy.get('.task-view .details.labels-list .multiselect .input-wrapper')
.children()
.first()
.get('[data-cy="taskDetail.removeLabel"]')
.get('a.delete')
.click()
cy.get('.global-notification')
@ -405,7 +405,7 @@ describe('Task', () => {
cy.get('.datepicker .datepicker-popup a')
.contains('Tomorrow')
.click()
cy.get('[data-cy="closeDatepicker"]')
cy.get('.datepicker .datepicker-popup a.button')
.contains('Confirm')
.click()

View File

@ -8,7 +8,7 @@ const testAndAssertFailed = fixture => {
cy.wait(5000) // It can take waaaayy too long to log the user in
cy.url().should('include', '/')
cy.get('div.message.danger').contains('Wrong username or password.')
cy.get('div.notification.is-danger').contains('Wrong username or password.')
}
context('Login', () => {

View File

@ -6,7 +6,7 @@ describe('Log out', () => {
cy.get('.navbar .user .username')
.click()
cy.get('.navbar .user .dropdown-menu .dropdown-item')
cy.get('.navbar .user .dropdown-menu a.dropdown-item')
.contains('Logout')
.click()

View File

@ -24,14 +24,15 @@ context('Registration', () => {
cy.visit('/register')
cy.get('#username').type(fixture.username)
cy.get('#email').type(fixture.email)
cy.get('#password').type(fixture.password)
cy.get('#password1').type(fixture.password)
cy.get('#password2').type(fixture.password)
cy.get('#register-submit').click()
cy.url().should('include', '/')
cy.clock(1625656161057) // 13:00
cy.get('h2').should('contain', `Hi ${fixture.username}!`)
})
it.only('Should fail', () => {
it('Should fail', () => {
const fixture = {
username: 'test',
password: '123456',
@ -41,8 +42,9 @@ context('Registration', () => {
cy.visit('/register')
cy.get('#username').type(fixture.username)
cy.get('#email').type(fixture.email)
cy.get('#password').type(fixture.password)
cy.get('#password1').type(fixture.password)
cy.get('#password2').type(fixture.password)
cy.get('#register-submit').click()
cy.get('div.message.danger').contains('A user with this username already exists.')
cy.get('div.notification.is-danger').contains('A user with this username already exists.')
})
})

View File

@ -8,37 +8,35 @@ describe('User Settings', () => {
})
it('Changes the user avatar', () => {
cy.intercept(`${Cypress.env('API_URL')}/user/settings/avatar/upload`).as('uploadAvatar')
cy.visit('/user/settings/avatar')
cy.visit('/user/settings')
cy.get('input[name=avatarProvider][value=upload]')
.click()
cy.get('input[type=file]', {timeout: 1000})
.selectFile('cypress/fixtures/image.jpg', {force: true}) // The input is not visible, but on purpose
cy.get('input[type=file]')
.attachFile('image.jpg')
cy.get('.vue-handler-wrapper.vue-handler-wrapper--south .vue-simple-handler.vue-simple-handler--south')
.trigger('mousedown', {which: 1})
.trigger('mousemove', {clientY: 100})
.trigger('mouseup')
cy.get('[data-cy="uploadAvatar"]')
cy.get('a.button.is-primary')
.contains('Upload Avatar')
.click()
cy.wait('@uploadAvatar')
cy.wait(3000) // Wait for the request to finish
cy.get('.global-notification')
.should('contain', 'Success')
})
it('Updates the name', () => {
cy.visit('/user/settings/general')
cy.visit('/user/settings')
cy.get('.general-settings .control input.input')
.first()
cy.get('input#newName')
.type('Lorem Ipsum')
cy.get('[data-cy="saveGeneralSettings"]')
cy.get('.card.general-settings .button.is-primary')
.contains('Save')
.click()
cy.wait(3000) // Wait for the request to finish
cy.get('.global-notification')
.should('contain', 'Success')
cy.get('.navbar .user .username')

View File

@ -1,4 +1,5 @@
import {seed} from './seed'
import merge from 'lodash/merge'
/**
* A factory makes it easy to seed the database with data.
@ -24,10 +25,7 @@ export class Factory {
const data = []
for (let i = 1; i <= count; i++) {
const entry = {
...this.factory(),
...override,
}
const entry = merge(this.factory(), override)
for (const e in entry) {
if(typeof entry[e] === 'function') {
entry[e] = entry[e](i)

View File

@ -1,10 +1,3 @@
import './commands'
import '@4tw/cypress-drag-drop'
// see https://github.com/cypress-io/cypress/issues/702#issuecomment-587127275
Cypress.on('window:before:load', (win) => {
// disable service workers
// @ts-ignore
delete win.navigator.__proto__.ServiceWorker
})
import 'cypress-file-upload'

View File

@ -1,17 +1,17 @@
image: vikunja/frontend:unstable
image: vikunja/frontend:latest
manifests:
-
image: vikunja/frontend:unstable-linux-amd64
image: vikunja/frontend:latest-linux-amd64
platform:
architecture: amd64
os: linux
-
image: vikunja/frontend:unstable-linux-arm64
image: vikunja/frontend:latest-linux-arm64
platform:
architecture: arm64
os: linux
-
image: vikunja/frontend:unstable-linux-arm
image: vikunja/frontend:latest-linux-arm
platform:
architecture: arm
os: linux

View File

@ -1,39 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Vikunja</title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<meta name="description" content="Vikunja (/vɪˈkuːnjə/) - The to-do app to organize your life.">
<meta name="theme-color" content="#1973ff"/>
<link rel="icon" href="/favicon.ico">
<link rel="apple-touch-icon" href="/images/icons/apple-touch-icon-180x180.png"/>
<link rel="preload" crossorigin="anonymous" href="/fonts/open-sans-v15-latin-700italic.woff2" as="font">
<link rel="preload" crossorigin="anonymous" href="/fonts/open-sans-v15-latin-italic.woff2" as="font">
<link rel="preload" crossorigin="anonymous" href="/fonts/quicksand-v7-latin-500.woff2" as="font">
<link rel="preload" crossorigin="anonymous" href="/fonts/quicksand-v7-latin-700.woff2" as="font">
<link rel="preload" crossorigin="anonymous" href="/fonts/open-sans-v15-latin-regular.woff2" as="font">
<link rel="preload" crossorigin="anonymous" href="/fonts/open-sans-v15-latin-700.woff2" as="font">
<link rel="preload" crossorigin="anonymous" href="/fonts/quicksand-v7-latin-regular.woff2" as="font">
</head>
<body>
<noscript>
<strong>We're sorry but Vikunja doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
<script>
//
// This variable points the frontend to the api.
// It has to be the full url, including the last /api/v1 part and port.
// You can change this if your api is not reachable on the same port as the frontend.
window.API_URL = 'http://localhost:3456/api/v1'
// Enable error tracking with sentry. If this is set to true, will send anonymized data to
// our sentry instance to notify us of potential problems.
window.SENTRY_ENABLED = false
window.SENTRY_DSN = 'https://85694a2d757547cbbc90cd4b55c5a18d@o1047380.ingest.sentry.io/6024480'
</script>
</body>
</html>

View File

@ -1,15 +0,0 @@
[build]
command = "yarn build"
publish = "dist-preview"
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
[[headers]]
for = "/*"
[headers.values]
X-Frame-Options = "DENY"
X-XSS-Protection = "1; mode=block"
X-Robots-Tag = "noindex"

View File

@ -6,110 +6,78 @@ pid /var/run/nginx.pid;
events {
worker_connections 1024;
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
include /etc/nginx/mime.types;
default_type application/octet-stream;
types {
application/manifest+json webmanifest;
}
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
keepalive_timeout 65;
gzip on;
gzip_disable "msie6";
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_min_length 256;
gzip_types text/plain text/css application/json application/x-javascript application/javascript text/xml application/xml application/xml+rss text/javascript application/vnd.ms-fontobject application/x-font-ttf font/opentype image/svg+xml font/woff2 image/x-icon audio/wav;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_min_length 256;
gzip_types
text/plain
text/css
application/json
application/x-javascript
application/javascript
text/xml
application/xml
application/xml+rss
text/javascript
application/vnd.ms-fontobject
application/x-font-ttf
font/opentype
image/svg+xml
image/x-icon
audio/wav;
map_hash_max_size 128;
map_hash_bucket_size 128;
map_hash_max_size 128;
map_hash_bucket_size 128;
# Expires map
map $sent_http_content_type $expires {
default off;
text/html max;
text/css max;
application/javascript max;
text/javascript max;
application/vnd.ms-fontobject max;
application/x-font-ttf max;
font/opentype max;
font/woff2 max;
image/svg+xml max;
image/x-icon max;
audio/wav max;
~image/ max;
~font/ max;
}
# Expires map
map $sent_http_content_type $expires {
default off;
text/css max;
application/javascript max;
text/javascript max;
application/vnd.ms-fontobject max;
application/x-font-ttf max;
font/opentype max;
font/woff2 max;
image/svg+xml max;
image/x-icon max;
audio/wav max;
~images/ max;
~font/ max;
}
server {
listen 80;
listen 81 default_server http2 proxy_protocol; ## Needed when behind HAProxy with SSL termination + HTTP/2 support
listen 443 default_server ssl http2;
server {
listen 80;
listen 81 default_server http2 proxy_protocol; ## Needed when behind HAProxy with SSL termination + HTTP/2 support
server_name _;
server_name _;
expires $expires;
expires $expires;
ssl_certificate /etc/nginx/ssl/dummy.crt;
ssl_certificate_key /etc/nginx/ssl/dummy.key;
root /usr/share/nginx/html;
location / {
root /usr/share/nginx/html;
try_files $uri $uri/ /;
index index.html index.htm;
}
# all assets contain hash in filename, cache forever
location ^~ /assets/ {
add_header Cache-Control "public, max-age=31536000, s-maxage=31536000, immutable";
try_files $uri =404;
}
# all workbox scripts are compiled with hash in filename, cache forever3
location ^~ /workbox- {
add_header Cache-Control "public, max-age=31536000, s-maxage=31536000, immutable";
try_files $uri =404;
}
# assume that everything else is handled by the application router, by injecting the index.html.
location / {
autoindex off;
expires off;
add_header Cache-Control "public, max-age=0, s-maxage=0, must-revalidate" always;
try_files $uri /index.html =404;
}
location ~* .(txt|webmanifest|css|js|mjs|map|svg|jpg|jpeg|png|ico|ttf|woff|woff2|wav)$ {
try_files $uri $uri/ =404;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
}

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

View File

@ -1,149 +0,0 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="16.000000pt" height="16.000000pt" viewBox="0 0 16.000000 16.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.11, written by Peter Selinger 2001-2013
</metadata>
<g transform="translate(0.000000,16.000000) scale(0.000320,-0.000320)"
fill="#000000" stroke="none">
<path d="M18 46618 c45 -75 122 -207 122 -211 0 -2 25 -45 55 -95 30 -50 55
-96 55 -102 0 -5 5 -10 10 -10 6 0 10 -4 10 -9 0 -5 73 -135 161 -288 89 -153
173 -298 187 -323 14 -25 32 -57 41 -72 88 -149 187 -324 189 -335 2 -7 8 -13
13 -13 5 0 9 -4 9 -10 0 -5 46 -89 103 -187 175 -302 490 -846 507 -876 8 -16
20 -36 25 -45 28 -46 290 -498 339 -585 13 -23 74 -129 136 -236 61 -107 123
-215 137 -240 14 -25 29 -50 33 -56 5 -5 23 -37 40 -70 18 -33 38 -67 44 -75
11 -16 21 -33 63 -109 14 -25 29 -50 33 -56 4 -5 21 -35 38 -65 55 -100 261
-455 269 -465 4 -5 14 -21 20 -35 15 -29 41 -75 103 -180 24 -41 52 -88 60
-105 9 -16 57 -100 107 -185 112 -193 362 -626 380 -660 8 -14 23 -38 33 -55
11 -16 23 -37 27 -45 4 -8 26 -46 48 -85 23 -38 53 -90 67 -115 46 -81 64
-113 178 -310 62 -107 121 -210 132 -227 37 -67 56 -99 85 -148 16 -27 32 -57
36 -65 4 -8 15 -27 25 -42 9 -15 53 -89 96 -165 44 -76 177 -307 296 -513 120
-206 268 -463 330 -570 131 -227 117 -203 200 -348 36 -62 73 -125 82 -140 10
-15 21 -34 25 -42 4 -8 20 -37 36 -65 17 -27 38 -65 48 -82 49 -85 64 -111 87
-153 13 -25 28 -49 32 -55 4 -5 78 -134 165 -285 87 -151 166 -288 176 -305
10 -16 26 -43 35 -59 9 -17 125 -217 257 -445 132 -229 253 -441 270 -471 17
-30 45 -79 64 -108 18 -29 33 -54 33 -57 0 -2 20 -37 44 -77 24 -40 123 -212
221 -383 97 -170 190 -330 205 -355 16 -25 39 -65 53 -90 13 -25 81 -144 152
-265 70 -121 137 -238 150 -260 12 -22 37 -65 55 -95 18 -30 43 -73 55 -95 12
-22 48 -85 80 -140 77 -132 163 -280 190 -330 13 -22 71 -123 130 -225 59
-102 116 -199 126 -217 10 -17 29 -50 43 -72 15 -22 26 -43 26 -45 0 -2 27
-50 60 -106 33 -56 60 -103 60 -105 0 -2 55 -98 90 -155 8 -14 182 -316 239
-414 13 -22 45 -79 72 -124 27 -46 49 -86 49 -89 0 -2 14 -24 30 -48 16 -24
30 -46 30 -49 0 -5 74 -135 100 -176 5 -8 24 -42 43 -75 50 -88 58 -101 262
-455 104 -179 199 -345 213 -370 14 -25 28 -49 32 -55 4 -5 17 -26 28 -45 10
-19 62 -109 114 -200 114 -197 133 -230 170 -295 16 -27 33 -57 38 -65 17 -28
96 -165 103 -180 4 -8 16 -28 26 -45 10 -16 77 -131 148 -255 72 -124 181
-313 243 -420 62 -107 121 -209 131 -227 35 -62 323 -560 392 -678 38 -66 83
-145 100 -175 16 -30 33 -59 37 -65 4 -5 17 -27 29 -47 34 -61 56 -100 90
-156 17 -29 31 -55 31 -57 0 -2 17 -32 39 -67 21 -35 134 -229 251 -433 117
-203 235 -407 261 -451 27 -45 49 -85 49 -88 0 -4 8 -19 19 -34 15 -21 200
-341 309 -533 10 -19 33 -58 51 -87 17 -29 31 -54 31 -56 0 -2 25 -44 55 -94
30 -50 55 -95 55 -98 0 -4 6 -15 14 -23 7 -9 27 -41 43 -71 17 -30 170 -297
342 -594 171 -296 311 -542 311 -547 0 -5 5 -9 10 -9 6 0 10 -4 10 -10 0 -5
22 -47 49 -92 27 -46 58 -99 68 -118 24 -43 81 -140 93 -160 5 -8 66 -114 135
-235 69 -121 130 -227 135 -235 12 -21 259 -447 283 -490 10 -19 28 -47 38
-62 11 -14 19 -29 19 -32 0 -3 37 -69 83 -148 99 -170 305 -526 337 -583 13
-22 31 -53 41 -70 11 -16 22 -37 26 -45 7 -14 82 -146 103 -180 14 -24 181
-311 205 -355 13 -22 46 -80 75 -130 29 -49 64 -110 78 -135 14 -25 51 -88 82
-140 31 -52 59 -102 63 -110 4 -8 18 -33 31 -55 205 -353 284 -489 309 -535
17 -30 45 -78 62 -106 18 -28 36 -60 39 -72 4 -12 12 -22 17 -22 5 0 9 -4 9
-10 0 -5 109 -197 241 -427 133 -230 250 -431 259 -448 51 -90 222 -385 280
-485 37 -63 78 -135 92 -160 14 -25 67 -117 118 -205 51 -88 101 -175 111
-193 34 -58 55 -95 149 -257 51 -88 101 -173 110 -190 9 -16 76 -131 147 -255
72 -124 140 -241 151 -260 61 -108 281 -489 355 -615 38 -66 77 -133 87 -150
35 -63 91 -161 100 -175 14 -23 99 -169 128 -220 54 -97 135 -235 142 -245 4
-5 20 -32 35 -60 26 -48 238 -416 276 -480 10 -16 26 -46 37 -65 30 -53 382
-661 403 -695 10 -16 22 -37 26 -45 4 -8 26 -48 50 -88 24 -41 43 -75 43 -77
0 -2 22 -40 50 -85 27 -45 50 -84 50 -86 0 -3 38 -69 83 -147 84 -142 302
-520 340 -587 10 -19 34 -60 52 -90 18 -30 44 -75 57 -100 14 -25 45 -79 70
-120 25 -41 56 -96 70 -121 14 -25 77 -133 138 -240 62 -107 122 -210 132
-229 25 -43 310 -535 337 -581 11 -19 26 -45 34 -59 17 -32 238 -414 266 -460
11 -19 24 -41 28 -49 3 -7 75 -133 160 -278 84 -146 153 -269 153 -274 0 -5 5
-9 10 -9 6 0 10 -4 10 -10 0 -5 82 -150 181 -322 182 -314 201 -346 240 -415
12 -21 80 -139 152 -263 71 -124 141 -245 155 -270 14 -25 28 -49 32 -55 6 -8
145 -248 220 -380 37 -66 209 -362 229 -395 11 -19 24 -42 28 -49 4 -8 67
-118 140 -243 73 -125 133 -230 133 -233 0 -2 15 -28 33 -57 19 -29 47 -78 64
-108 17 -30 53 -93 79 -139 53 -90 82 -141 157 -272 82 -142 115 -199 381
-659 142 -245 268 -463 281 -485 12 -22 71 -125 132 -230 60 -104 172 -298
248 -430 76 -132 146 -253 156 -270 11 -16 22 -36 26 -44 3 -8 30 -54 60 -103
29 -49 53 -91 53 -93 0 -3 18 -34 40 -70 22 -36 40 -67 40 -69 0 -2 37 -66 81
-142 45 -77 98 -168 119 -204 20 -36 47 -81 58 -100 12 -19 27 -47 33 -62 6
-16 15 -28 20 -28 5 0 9 -4 9 -9 0 -6 63 -118 140 -251 77 -133 140 -243 140
-245 0 -2 18 -33 41 -70 22 -37 49 -83 60 -101 10 -19 29 -51 40 -71 25 -45
109 -189 126 -218 7 -11 17 -29 22 -40 6 -11 22 -38 35 -60 14 -22 37 -62 52
-90 14 -27 35 -62 45 -77 11 -14 19 -29 19 -32 0 -3 18 -35 40 -71 22 -36 40
-67 40 -69 0 -2 19 -35 42 -72 23 -38 55 -94 72 -124 26 -47 139 -244 171
-298 6 -9 21 -36 34 -60 28 -48 37 -51 51 -19 6 12 19 36 29 52 10 17 27 46
38 65 11 19 104 181 208 360 103 179 199 345 213 370 14 25 42 74 64 109 21
34 38 65 38 67 0 2 18 33 40 69 22 36 40 67 40 69 0 3 177 310 199 346 16 26
136 234 140 244 2 5 25 44 52 88 27 44 49 81 49 84 0 2 18 34 40 70 22 36 40
67 40 69 0 2 20 36 43 77 35 58 169 289 297 513 9 17 50 86 90 155 40 69 86
150 103 180 16 30 35 62 41 70 6 8 16 24 22 35 35 64 72 129 167 293 59 100
116 199 127 220 11 20 30 53 41 72 43 72 1070 1850 1121 1940 14 25 65 113
113 195 48 83 96 166 107 185 10 19 28 50 38 68 11 18 73 124 137 235 64 111
175 303 246 427 71 124 173 299 225 390 52 91 116 202 143 248 27 45 49 85 49
89 0 4 6 14 14 22 7 9 28 43 46 76 26 47 251 436 378 655 11 19 29 51 40 70
11 19 101 176 201 348 99 172 181 317 181 323 0 5 5 9 10 9 6 0 10 5 10 11 0
6 8 23 18 37 11 15 32 52 49 82 16 30 130 228 253 440 122 212 234 405 248
430 13 25 39 70 57 100 39 65 69 117 130 225 25 44 50 87 55 95 12 19 78 134
220 380 61 107 129 224 150 260 161 277 222 382 246 425 15 28 47 83 71 123
24 41 43 78 43 83 0 5 4 9 8 9 4 0 13 12 19 28 7 15 23 45 36 67 66 110 277
478 277 483 0 3 6 13 14 21 7 9 27 41 43 71 17 30 45 80 63 110 34 57 375 649
394 685 6 11 16 27 22 35 6 8 26 42 44 75 18 33 41 74 51 90 10 17 24 41 32
55 54 97 72 128 88 152 11 14 19 28 19 30 0 3 79 141 175 308 96 167 175 305
175 308 0 3 6 13 14 21 7 9 26 39 41 66 33 60 276 483 338 587 24 40 46 80 50
88 4 8 13 24 20 35 14 23 95 163 125 215 11 19 52 91 92 160 40 69 80 139 90
155 9 17 103 179 207 360 105 182 200 346 211 365 103 181 463 802 489 845 7
11 15 27 19 35 4 8 29 51 55 95 64 110 828 1433 848 1470 9 17 24 41 33 55 9
14 29 48 45 77 15 28 52 93 82 145 30 51 62 107 71 123 17 30 231 398 400 690
51 88 103 179 115 202 12 23 26 48 32 55 6 7 24 38 40 68 17 30 61 107 98 170
37 63 84 144 103 180 19 36 41 72 48 81 8 8 14 18 14 21 0 4 27 51 59 106 32
55 72 124 89 154 16 29 71 125 122 213 51 88 104 180 118 205 13 25 28 50 32
55 4 6 17 26 28 45 11 19 45 80 77 135 31 55 66 116 77 135 11 19 88 152 171
295 401 694 620 1072 650 1125 11 19 87 152 170 295 83 143 158 273 166 288 9
16 21 36 26 45 6 9 31 52 55 96 25 43 54 94 66 115 11 20 95 164 186 321 91
157 173 299 182 315 9 17 26 46 37 65 12 19 66 114 121 210 56 96 108 186 117
200 8 14 24 40 34 59 24 45 383 664 412 713 5 9 17 29 26 45 15 28 120 210
241 419 36 61 68 117 72 125 4 8 12 23 19 34 35 57 245 420 262 453 11 20 35
61 53 90 17 29 32 54 32 56 0 3 28 51 62 108 33 57 70 119 80 138 10 19 23 42
28 50 5 8 32 53 59 100 27 47 149 258 271 470 122 212 234 405 248 430 30 53
62 108 80 135 6 11 15 27 19 35 4 8 85 150 181 315 96 165 187 323 202 350 31
56 116 202 130 225 5 8 25 42 43 75 19 33 92 159 162 280 149 257 157 271 202
350 19 33 38 67 43 75 9 14 228 392 275 475 12 22 55 96 95 165 40 69 80 139
90 155 24 42 202 350 221 383 9 15 27 47 41 72 14 25 75 131 136 236 61 106
121 210 134 232 99 172 271 470 279 482 5 8 23 40 40 70 18 30 81 141 142 245
60 105 121 210 135 235 14 25 71 124 127 220 56 96 143 247 194 335 51 88 96
167 102 175 14 24 180 311 204 355 23 43 340 590 356 615 5 8 50 87 101 175
171 301 517 898 582 1008 25 43 46 81 46 83 0 2 12 23 27 47 14 23 40 67 56
97 16 30 35 62 42 70 7 8 15 22 18 30 4 8 20 38 37 65 16 28 33 57 37 65 6 12
111 196 143 250 5 8 55 95 112 193 57 98 113 195 126 215 12 20 27 46 32 57 6
11 14 27 20 35 5 8 76 130 156 270 80 140 165 287 187 325 23 39 52 90 66 115
13 25 30 52 37 61 8 8 14 18 14 21 0 4 41 77 92 165 50 87 175 302 276 478
101 176 208 360 236 408 28 49 67 117 86 152 19 35 41 70 48 77 6 6 12 15 12
19 0 7 124 224 167 291 12 21 23 40 23 42 0 2 21 40 46 83 26 43 55 92 64 109
54 95 327 568 354 614 19 30 45 75 59 100 71 128 82 145 89 148 4 2 8 8 8 13
0 5 42 82 94 172 311 538 496 858 518 897 14 25 40 70 58 100 18 30 42 71 53
90 10 19 79 139 152 265 73 127 142 246 153 265 10 19 43 76 72 125 29 50 63
108 75 130 65 116 80 140 87 143 4 2 8 8 8 12 0 8 114 212 140 250 6 8 14 24
20 35 5 11 54 97 108 190 l100 170 -9611 3 c-5286 1 -9614 -1 -9618 -5 -5 -6
-419 -719 -619 -1068 -89 -155 -267 -463 -323 -560 -38 -66 -81 -140 -95 -165
-31 -56 -263 -457 -526 -910 -110 -190 -224 -388 -254 -440 -29 -52 -61 -109
-71 -125 -23 -39 -243 -420 -268 -465 -11 -19 -204 -352 -428 -740 -224 -388
-477 -826 -563 -975 -85 -148 -185 -322 -222 -385 -37 -63 -120 -207 -185
-320 -65 -113 -177 -306 -248 -430 -72 -124 -172 -297 -222 -385 -51 -88 -142
-245 -202 -350 -131 -226 -247 -427 -408 -705 -65 -113 -249 -432 -410 -710
-160 -278 -388 -673 -506 -877 -118 -205 -216 -373 -219 -373 -3 0 -52 82
-109 183 -58 100 -144 250 -192 332 -95 164 -402 696 -647 1120 -85 149 -228
396 -317 550 -212 365 -982 1700 -1008 1745 -10 19 -43 76 -72 125 -29 50 -64
110 -77 135 -14 25 -63 110 -110 190 -47 80 -96 165 -110 190 -14 25 -99 171
-188 325 -89 154 -174 300 -188 325 -13 25 -64 113 -112 195 -48 83 -140 242
-205 355 -65 113 -183 317 -263 454 -79 137 -152 264 -163 282 -50 89 -335
583 -354 614 -12 19 -34 58 -50 85 -15 28 -129 226 -253 440 -124 215 -235
408 -247 430 -12 22 -69 121 -127 220 -58 99 -226 389 -373 645 -148 256 -324
561 -392 678 -67 117 -134 232 -147 255 -13 23 -33 59 -46 80 l-22 37 -9615 0
-9615 0 20 -32z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 10 KiB

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1007.922 821.827"><defs><style>.a{fill:#fff;}.b{fill:url(#a);}.c{mask:url(#b);}.d{fill:url(#c);}.e{mask:url(#d);}.f{fill:url(#e);}.g{fill:url(#f);}.h{mask:url(#g);}.i{fill:url(#h);}.j{fill:#195abd;}.k{fill:url(#i);}</style><linearGradient id="a" x1="700.766" y1="597.024" x2="749.765" y2="597.024" gradientTransform="translate(86.603 -142.296) scale(0.867 1.307)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-opacity="0.13"/><stop offset="0.994" stop-opacity="0"/></linearGradient><mask id="b" x="317.137" y="651.827" width="170" height="205.208" maskUnits="userSpaceOnUse"><g transform="translate(-8.064 -116.521)"><rect class="a" x="367.701" y="870.953" width="85" height="85" transform="translate(766.054 -22.512) rotate(45)"/></g></mask><radialGradient id="c" cx="410.201" cy="853.349" r="85" gradientTransform="translate(715.49 -156.637) rotate(45)" gradientUnits="userSpaceOnUse"><stop offset="0.5" stop-opacity="0.13"/><stop offset="0.994" stop-opacity="0"/></radialGradient><mask id="d" x="837.922" y="95.835" width="205.208" height="205.208" maskUnits="userSpaceOnUse"><g transform="translate(-8.064 -116.521)"><rect class="a" x="876.038" y="260.012" width="170" height="85" transform="translate(1426.685 1195.977) rotate(-135)"/></g></mask><radialGradient id="e" cx="1051.126" cy="1265.852" r="85" gradientTransform="translate(771.087 1854.394) rotate(-135)" xlink:href="#c"/><linearGradient id="f" x1="1880.8" y1="34.286" x2="1929.799" y2="34.286" gradientTransform="matrix(0.867, 0, 0, -0.796, -1446.031, 767.147)" xlink:href="#a"/><mask id="g" x="-35.208" y="299.482" width="205.208" height="205.208" maskUnits="userSpaceOnUse"><g transform="translate(-8.064 -116.521)"><rect class="a" x="-21.988" y="463.659" width="170" height="85" transform="translate(-339.453 192.807) rotate(-45)"/></g></mask><radialGradient id="h" cx="27.608" cy="2001.37" r="85" gradientTransform="matrix(0.707, -0.707, -0.707, -0.707, 1480.66, 1854.394)" xlink:href="#c"/><linearGradient id="i" x1="308.378" y1="811.629" x2="919.318" y2="200.689" gradientTransform="translate(-178.117 582.307) rotate(-45)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#2987e6"/><stop offset="0.994" stop-color="#58c1f5"/></linearGradient></defs><title>Todo</title><rect class="b" x="694.422" y="269.785" width="42.5" height="736.5" transform="translate(652.71 -435.701) rotate(45)"/><g class="c"><circle class="d" cx="402.137" cy="736.827" r="85"/></g><g class="e"><circle class="f" cx="922.922" cy="216.043" r="85"/></g><rect class="g" x="185.305" y="515.608" width="42.5" height="448.5" transform="translate(867.705 1000.439) rotate(135)"/><g class="h"><circle class="i" cx="85" cy="419.69" r="85"/></g><rect class="j" x="164.378" y="319.982" width="288" height="576" rx="42.5" transform="translate(-347.651 279.609) rotate(-45)"/><rect class="k" x="469.848" y="74.159" width="288" height="864" rx="42.5" transform="translate(529.636 -402.327) rotate(45)"/></svg>

Before

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="256px" height="256px" viewBox="0 0 256 256" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
<g>
<path d="M224.001997,0 L31.9980026,0 C14.3579381,0.0394964443 0.0614809418,14.336846 0,32 L0,224 C0,241.6 14.3971038,256 31.9980026,256 L224.001997,256 C241.602896,256 256,241.6 256,224 L256,32 C256,14.4 241.602896,0 224.001997,0" fill="#E44332"></path>
<path d="M54.132778,120.802491 C58.5960224,118.196275 154.476075,62.477451 156.667847,61.1862981 C158.859619,59.9110855 158.97917,55.9898065 156.508446,54.5711324 C154.053661,53.1604284 149.391165,50.4824817 147.661658,49.4543415 C145.192242,48.0957707 142.191169,48.132074 139.755339,49.5499825 C138.527947,50.2672896 56.6035026,97.8486625 53.8697654,99.4107981 C50.5781227,101.291737 46.5372925,101.323617 43.2695601,99.4107981 L0,74.0181257 L0,95.6011002 C10.5205046,101.801822 36.7181549,117.200015 43.062338,120.826401 C46.8481256,122.978322 50.4745117,122.930502 54.1407481,120.802491" fill="#FFFFFF"></path>
<path d="M54.132778,161.609296 C58.5960224,159.00308 154.476075,103.284257 156.667847,101.993104 C158.859619,100.717891 158.97917,96.7966121 156.508446,95.377938 C154.053661,93.9672339 149.391165,91.2892873 147.661658,90.2611471 C145.192242,88.9025763 142.191169,88.9388796 139.755339,90.3567881 C138.527947,91.0740952 56.6035026,138.655468 53.8697654,140.217604 C50.5781227,142.098542 46.5372925,142.130423 43.2695601,140.217604 L0,114.824931 L0,136.407906 C10.5205046,142.608627 36.7181549,158.00682 43.062338,161.633206 C46.8481256,163.785128 50.4745117,163.737307 54.1407481,161.609296" fill="#FFFFFF"></path>
<path d="M54.132778,204.966527 C58.5960224,202.360311 154.476075,146.641487 156.667847,145.350335 C158.859619,144.075122 158.97917,140.153843 156.508446,138.735169 C154.053661,137.324465 149.391165,134.646518 147.661658,133.618378 C145.192242,132.259807 142.191169,132.29611 139.755339,133.714019 C138.527947,134.431326 56.6035026,182.012699 53.8697654,183.574835 C50.5781227,185.455773 46.5372925,185.487654 43.2695601,183.574835 L0,158.182162 L0,179.765137 C10.5205046,185.965858 36.7181549,201.364051 43.062338,204.990437 C46.8481256,207.142359 50.4745117,207.094538 54.1407481,204.966527" fill="#FFFFFF"></path>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="256px" height="256px" viewBox="0 0 256 256" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
<defs>
<linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient-1">
<stop stop-color="#0091E6" offset="0%"></stop>
<stop stop-color="#0079BF" offset="100%"></stop>
</linearGradient>
</defs>
<g>
<g>
<rect fill="url(#linearGradient-1)" x="0" y="0" width="256" height="256" rx="25"></rect>
<rect fill="#FFFFFF" x="144.64" y="33.28" width="78.08" height="112" rx="12"></rect>
<rect fill="#FFFFFF" x="33.28" y="33.28" width="78.08" height="176" rx="12"></rect>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 829 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

View File

@ -3,148 +3,96 @@
"version": "0.10.0",
"private": true,
"scripts": {
"serve": "vite",
"serve:dist-dev": "node scripts/serve-dist.js",
"serve:dist": "vite preview --port 4173",
"build": "vite build && workbox copyLibraries dist/",
"build:modern-only": "BUILD_MODERN_ONLY=true vite build && workbox copyLibraries dist/",
"build:dev": "vite build -m development --outDir dist-dev/",
"typecheck": "vue-tsc --noEmit",
"lint": "eslint --ignore-pattern '*.test.*' ./src --ext .vue,.js,.ts",
"serve": "vue-cli-service serve",
"serve:dist": "node scripts/serve-dist.js",
"build": "vue-cli-service build --modern",
"build:report": "vue-cli-service build --report",
"lint": "vue-cli-service lint --ignore-pattern '*.test.*'",
"cypress:open": "cypress open",
"test:unit": "vitest run",
"test:unit-watch": "vitest watch",
"test:frontend": "cypress run",
"browserslist:update": "npx browserslist@latest --update-db"
"test:unit": "jest",
"test:frontend": "cypress run"
},
"dependencies": {
"@github/hotkey": "2.0.0",
"@kyvg/vue3-notification": "2.3.4",
"@sentry/tracing": "6.19.7",
"@sentry/vue": "6.19.7",
"@types/is-touch-device": "1.0.0",
"@types/sortablejs": "1.13.0",
"@vue/compat": "3.2.31",
"@vueuse/core": "8.4.2",
"@vueuse/router": "8.4.2",
"blurhash": "1.1.5",
"bulma-css-variables": "0.9.33",
"browserslist": "4.16.6",
"bulma": "0.9.3",
"camel-case": "4.1.2",
"codemirror": "5.65.3",
"date-fns": "2.28.0",
"dompurify": "2.3.6",
"easymde": "2.16.1",
"flatpickr": "4.6.13",
"flexsearch": "0.7.21",
"highlight.js": "11.5.1",
"is-touch-device": "1.0.1",
"lodash.clonedeep": "4.5.0",
"lodash.debounce": "4.0.8",
"marked": "4.0.15",
"minimist": "1.2.6",
"copy-to-clipboard": "3.3.1",
"date-fns": "2.22.1",
"dompurify": "2.3.0",
"highlight.js": "11.0.1",
"lodash": "4.17.21",
"marked": "2.1.3",
"register-service-worker": "1.7.2",
"sass": "1.35.2",
"snake-case": "3.0.4",
"ufo": "0.8.4",
"v-tooltip": "4.0.0-beta.17",
"vue": "3.2.33",
"vue-advanced-cropper": "2.8.1",
"vue-drag-resize": "2.0.3",
"vue-flatpickr-component": "9.0.6",
"vue-i18n": "9.2.0-beta.30",
"vue-router": "4.0.15",
"vuedraggable": "4.1.0",
"vuex": "4.0.2",
"workbox-precaching": "6.5.3"
"verte": "0.0.12",
"vue": "2.6.14",
"vue-advanced-cropper": "1.7.0",
"vue-drag-resize": "1.5.4",
"vue-easymde": "1.4.0",
"vue-i18n": "8.24.5",
"vue-shortkey": "3.1.7",
"vue-smooth-dnd": "0.8.1",
"vuex": "3.6.2"
},
"devDependencies": {
"@4tw/cypress-drag-drop": "2.1.0",
"@faker-js/faker": "6.3.1",
"@fortawesome/fontawesome-svg-core": "6.1.1",
"@fortawesome/free-regular-svg-icons": "6.1.1",
"@fortawesome/free-solid-svg-icons": "6.1.1",
"@fortawesome/vue-fontawesome": "3.0.0-5",
"@types/flexsearch": "0.7.3",
"@typescript-eslint/eslint-plugin": "5.23.0",
"@typescript-eslint/parser": "5.23.0",
"@vitejs/plugin-legacy": "1.8.2",
"@vitejs/plugin-vue": "2.3.2",
"@vue/eslint-config-typescript": "10.0.0",
"autoprefixer": "10.4.7",
"axios": "0.27.2",
"browserslist": "4.20.3",
"caniuse-lite": "1.0.30001338",
"cypress": "9.6.1",
"esbuild": "0.14.38",
"eslint": "8.15.0",
"eslint-plugin-vue": "8.7.1",
"express": "4.18.1",
"happy-dom": "3.1.1",
"netlify-cli": "10.3.0",
"postcss": "8.4.13",
"postcss-preset-env": "7.5.0",
"rollup": "2.72.1",
"rollup-plugin-visualizer": "5.6.0",
"sass": "1.51.0",
"typescript": "4.6.4",
"vite": "2.9.8",
"vite-plugin-pwa": "0.12.0",
"vite-svg-loader": "3.3.0",
"vitest": "0.10.5",
"vue-tsc": "0.34.12",
"wait-on": "6.0.1",
"workbox-cli": "6.5.3"
"@fortawesome/fontawesome-svg-core": "1.2.35",
"@fortawesome/free-regular-svg-icons": "5.15.3",
"@fortawesome/free-solid-svg-icons": "5.15.3",
"@fortawesome/vue-fontawesome": "2.0.2",
"@vue/cli": "4.5.13",
"@vue/cli-plugin-babel": "4.5.13",
"@vue/cli-plugin-eslint": "4.5.13",
"@vue/cli-plugin-pwa": "4.5.13",
"@vue/cli-service": "4.5.13",
"axios": "0.21.1",
"babel-eslint": "10.1.0",
"cypress": "7.7.0",
"cypress-file-upload": "5.0.8",
"eslint": "7.30.0",
"eslint-plugin-vue": "7.13.0",
"faker": "5.5.3",
"jest": "27.0.6",
"sass-loader": "10.2.0",
"vue-flatpickr-component": "8.1.7",
"vue-notification": "1.3.20",
"vue-router": "3.5.2",
"vue-template-compiler": "2.6.14",
"wait-on": "6.0.0"
},
"eslintConfig": {
"root": true,
"env": {
"browser": true,
"es2021": true,
"node": true,
"vue/setup-compiler-macros": true
"node": true
},
"extends": [
"eslint:recommended",
"plugin:vue/vue3-essential",
"@vue/typescript"
"plugin:vue/essential",
"eslint:recommended"
],
"rules": {
"vue/html-quotes": [
"error",
"double"
],
"quotes": [
"error",
"single"
],
"comma-dangle": [
"error",
"always-multiline"
],
"semi": [
"error",
"never"
],
"vue/script-setup-uses-vars": "error",
"vue/multi-word-component-names": 0
},
"parser": "vue-eslint-parser",
"rules": {},
"parserOptions": {
"parser": "@typescript-eslint/parser",
"ecmaVersion": 2022
"parser": "babel-eslint"
},
"ignorePatterns": [
"*.test.*",
"*.test.js",
"cypress/*"
],
"globals": {
"defineProps": "readonly"
}
]
},
"postcss": {
"plugins": {
"autoprefixer": {}
}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie < 11"
],
"license": "AGPL-3.0-or-later",
"packageManager": "yarn@1.22.18"
"jest": {
"testPathIgnorePatterns": [
"cypress"
],
"testEnvironment": "jsdom"
}
}

8
ping-weblate.sh Executable file
View File

@ -0,0 +1,8 @@
#!/bin/sh
set -e
# Shell script because yaml doesn't understand the header is a string literal and not a yaml symbol
curl -d operation=pull -H "Authorization: Token $WEBLATE_TOKEN" https://hosted.weblate.org/api/projects/vikunja/repository/
curl -d operation=push -H "Authorization: Token $WEBLATE_TOKEN" https://hosted.weblate.org/api/projects/vikunja/repository/

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -49,7 +49,7 @@
inkscape:label="ink_ext_XXXXXX 1"
style="display:inline"
transform="translate(-92.67749,-674.48297)"><circle
style="fill:#196aff;fill-opacity:1;stroke:none;stroke-width:2.88757133;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
style="fill:#5974d9;fill-opacity:1;stroke:none;stroke-width:2.88757133;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="path920"
cx="242.67749"
cy="828.77881"

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 616 B

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 126 KiB

After

Width:  |  Height:  |  Size: 126 KiB

View File

@ -16,6 +16,9 @@
height="1066.6667"
viewBox="0 0 1066.6667 1066.6667"
sodipodi:docname="llama-nightscape.svg"
inkscape:export-filename="/home/konrad/www/vikunja/frontend/public/images/llama-nightscape.png"
inkscape:export-xdpi="172.8"
inkscape:export-ydpi="172.8"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"><metadata
id="metadata8"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type

Before

Width:  |  Height:  |  Size: 174 KiB

After

Width:  |  Height:  |  Size: 174 KiB

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