Compare commits

...

47 Commits

Author SHA1 Message Date
simon1506 8a9a5576c6
fix: no drag delay when using mouse on touch device 2021-09-13 18:26:56 +02:00
dpschen 50c1a2e4d5 feat simplify taskList mixin (#728)
Co-authored-by: Dominik Pschenitschni <mail@celement.de>
Co-authored-by: kolaente <k@knt.li>
Reviewed-on: vikunja/frontend#728
Reviewed-by: konrad <k@knt.li>
Co-authored-by: dpschen <dpschen@noreply.kolaente.de>
Co-committed-by: dpschen <dpschen@noreply.kolaente.de>
2021-09-10 14:39:18 +00:00
dpschen 07a6a31f47 chore: move constants in folder (#732)
Co-authored-by: Dominik Pschenitschni <mail@celement.de>
Co-authored-by: kolaente <k@knt.li>
Reviewed-on: vikunja/frontend#732
Reviewed-by: konrad <k@knt.li>
Co-authored-by: dpschen <dpschen@noreply.kolaente.de>
Co-committed-by: dpschen <dpschen@noreply.kolaente.de>
2021-09-10 14:21:33 +00:00
dpschen 0295113f50 feat: use store getters to check auth (#731)
Co-authored-by: Dominik Pschenitschni <mail@celement.de>
Reviewed-on: vikunja/frontend#731
Reviewed-by: konrad <k@knt.li>
Co-authored-by: dpschen <dpschen@noreply.kolaente.de>
Co-committed-by: dpschen <dpschen@noreply.kolaente.de>
2021-09-10 13:04:00 +00:00
dpschen b5df941e39 chore: define default filters and params at one location (#721)
Co-authored-by: Dominik Pschenitschni <mail@celement.de>
Reviewed-on: vikunja/frontend#721
Reviewed-by: konrad <k@knt.li>
Co-authored-by: dpschen <dpschen@noreply.kolaente.de>
Co-committed-by: dpschen <dpschen@noreply.kolaente.de>
2021-09-10 13:02:52 +00:00
konrad 077fe264f0 fix: use date-fns for gantt years (#734)
Co-authored-by: kolaente <k@knt.li>
Reviewed-on: vikunja/frontend#734
Co-authored-by: konrad <k@knt.li>
Co-committed-by: konrad <k@knt.li>
2021-09-10 12:58:23 +00:00
dpschen dae441a373 feat: simplify heading blur logic (#727)
Co-authored-by: Dominik Pschenitschni <mail@celement.de>
Reviewed-on: vikunja/frontend#727
Reviewed-by: konrad <k@knt.li>
Co-authored-by: dpschen <dpschen@noreply.kolaente.de>
Co-committed-by: dpschen <dpschen@noreply.kolaente.de>
2021-09-10 12:57:59 +00:00
dpschen 0376ef53e3 fix: remove attachment by id (#725)
Co-authored-by: Dominik Pschenitschni <mail@celement.de>
Reviewed-on: vikunja/frontend#725
Co-authored-by: dpschen <dpschen@noreply.kolaente.de>
Co-committed-by: dpschen <dpschen@noreply.kolaente.de>
2021-09-10 12:50:41 +00:00
renovate 7ab308b846 chore(deps): update workbox monorepo to v6.3.0 (#730)
Co-authored-by: kolaente <k@knt.li>
Reviewed-on: vikunja/frontend#730
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2021-09-10 12:49:50 +00:00
renovate 33447c4a09 chore(deps): update dependency sass to v1.39.2 (#733)
Reviewed-on: vikunja/frontend#733
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2021-09-10 11:13:54 +00:00
dpschen d0e46e59e8 chore: make method event independent (#719)
Co-authored-by: Dominik Pschenitschni <mail@celement.de>
Reviewed-on: vikunja/frontend#719
Co-authored-by: dpschen <dpschen@noreply.kolaente.de>
Co-committed-by: dpschen <dpschen@noreply.kolaente.de>
2021-09-09 22:25:08 +00:00
dpschen 0ed3cf2553 feat: import bulma utilities global (#718)
Co-authored-by: Dominik Pschenitschni <mail@celement.de>
Reviewed-on: vikunja/frontend#718
Co-authored-by: dpschen <dpschen@noreply.kolaente.de>
Co-committed-by: dpschen <dpschen@noreply.kolaente.de>
2021-09-09 22:24:08 +00:00
renovate 33f1480284 chore(deps): update dependency esbuild to v0.12.26 (#729)
Reviewed-on: vikunja/frontend#729
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2021-09-09 16:17:12 +00:00
dpschen 432c6babf2 feat: use computed for api domain (#722)
Co-authored-by: Dominik Pschenitschni <mail@celement.de>
Reviewed-on: vikunja/frontend#722
Co-authored-by: dpschen <dpschen@noreply.kolaente.de>
Co-committed-by: dpschen <dpschen@noreply.kolaente.de>
2021-09-08 21:48:30 +00:00
dpschen 96ef926dde chore: create progress dots dynamically (#715)
Co-authored-by: Dominik Pschenitschni <mail@celement.de>
Reviewed-on: vikunja/frontend#715
Co-authored-by: dpschen <dpschen@noreply.kolaente.de>
Co-committed-by: dpschen <dpschen@noreply.kolaente.de>
2021-09-08 21:45:28 +00:00
dpschen 87c70cec0e chore: define default label background color once (#713)
Co-authored-by: Dominik Pschenitschni <mail@celement.de>
Reviewed-on: vikunja/frontend#713
Co-authored-by: dpschen <dpschen@noreply.kolaente.de>
Co-committed-by: dpschen <dpschen@noreply.kolaente.de>
2021-09-08 21:44:56 +00:00
renovate 4689de7f1f fix(deps): update dependency marked to v3.0.3 (#726)
Reviewed-on: vikunja/frontend#726
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2021-09-08 21:19:21 +00:00
renovate b0f616d784 [skip ci] Updated translations via Crowdin 2021-09-08 20:36:21 +00:00
renovate 11b5d0574d chore(deps): update dependency vite to v2.5.6 (#723)
Reviewed-on: vikunja/frontend#723
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2021-09-08 20:31:09 +00:00
kolaente 134a09c9f2
Fix download export user data title 2021-09-08 22:19:45 +02:00
kolaente c7c3ef79be
0.18.1 release preparations 2021-09-08 19:49:23 +02:00
kolaente 2bae8e95e5
Fix task attributes overridden when saving the task title with enter 2021-09-08 19:37:08 +02:00
dpschen c4095327ad feat: make it possible to fake online state via dev env (#720)
Co-authored-by: Dominik Pschenitschni <mail@celement.de>
Reviewed-on: vikunja/frontend#720
Co-authored-by: dpschen <dpschen@noreply.kolaente.de>
Co-committed-by: dpschen <dpschen@noreply.kolaente.de>
2021-09-08 16:30:14 +00:00
dpschen c9631c1e71 fix: call to /null from background image (#714)
Co-authored-by: Dominik Pschenitschni <mail@celement.de>
Reviewed-on: vikunja/frontend#714
Co-authored-by: dpschen <dpschen@noreply.kolaente.de>
Co-committed-by: dpschen <dpschen@noreply.kolaente.de>
2021-09-08 16:28:26 +00:00
dpschen 4fc8858c64 fix: kanban-card mutatation violation (#712)
Co-authored-by: Dominik Pschenitschni <mail@celement.de>
Reviewed-on: vikunja/frontend#712
Reviewed-by: konrad <k@knt.li>
Co-authored-by: dpschen <dpschen@noreply.kolaente.de>
Co-committed-by: dpschen <dpschen@noreply.kolaente.de>
2021-09-08 16:28:13 +00:00
kolaente 9cee720ac9
Fix sort order for table view 2021-09-08 18:13:02 +02:00
kolaente 6f89863c81
Fix missing translation when creating a new task on the kanban board 2021-09-08 17:49:10 +02:00
kolaente debdc83f1b
Fix data export download progress 2021-09-08 17:33:58 +02:00
renovate 033b30d6cd Update dependency @4tw/cypress-drag-drop to v2 (#711)
Reviewed-on: vikunja/frontend#711
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2021-09-08 14:27:47 +00:00
renovate c23d3c1488 Update dependency jest to v27.1.1 (#716)
Reviewed-on: vikunja/frontend#716
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2021-09-08 14:27:24 +00:00
renovate 4f305b28fd Update dependency vite to v2.5.5 (#709)
Reviewed-on: vikunja/frontend#709
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2021-09-08 05:43:13 +00:00
renovate 33926a2d11 Update dependency vite to v2.5.4 (#708)
Reviewed-on: vikunja/frontend#708
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2021-09-07 17:44:41 +00:00
kolaente 332dbc1598
Fix rearranging tasks in a kanban bucket when its limit was reached 2021-09-07 18:38:53 +02:00
renovate 28a4b1c533 Update dependency vite-plugin-vue2 to v1.8.2 (#707)
Reviewed-on: vikunja/frontend#707
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2021-09-07 16:17:41 +00:00
kolaente 78346f9ac6
Fix translation badge 2021-09-06 21:22:18 +02:00
renovate 8f916c275b Update typescript-eslint monorepo to v4.31.0 (#706)
Reviewed-on: vikunja/frontend#706
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2021-09-06 19:02:48 +00:00
renovate cb0e2a7bc1 Update dependency axios to v0.21.4 (#705)
Reviewed-on: vikunja/frontend#705
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2021-09-06 16:53:46 +00:00
kolaente 2d804c3af3
0.18.0 release preparations 2021-09-05 17:07:51 +02:00
renovate 6868fb134d Update dependency browserslist to v4.17.0 (#701)
Reviewed-on: vikunja/frontend#701
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2021-09-05 12:40:41 +00:00
kolaente b7ec975384
Change building latest docker image 2021-09-05 14:40:14 +02:00
kolaente 75174c2496
Switch the :latest docker image tag to contain the latest release instead of the latest unstable 2021-09-05 13:44:05 +02:00
kolaente e0c9332634
Add proofread languages to available languages 2021-09-05 13:31:53 +02:00
kolaente 7a4e568898 [skip ci] Updated translations via Crowdin 2021-09-05 11:10:28 +00:00
kolaente fb20afae92
Move translated files after downloading them 2021-09-05 13:06:45 +02:00
kolaente db0e023d35 [skip ci] Updated translations via Crowdin 2021-09-05 11:03:35 +00:00
kolaente 13f01cdc30
Add depends_on for push step 2021-09-05 13:03:15 +02:00
kolaente b4919a5662
Add depends_on for upload step 2021-09-05 13:02:43 +02:00
68 changed files with 1597 additions and 2089 deletions

View File

@ -341,7 +341,7 @@ trigger:
- "refs/tags/**"
steps:
- name: docker-latest
- name: docker-unstable
image: plugins/docker:linux-arm
pull: true
settings:
@ -350,7 +350,7 @@ steps:
password:
from_secret: docker_password
repo: vikunja/frontend
tags: latest-linux-arm
tags: unstable-linux-arm
build_args:
- USE_RELEASE=true
- RELEASE_VERSION=unstable
@ -380,7 +380,7 @@ steps:
depends_on:
- clone
- name: docker-latest-arm64
- name: docker-unstable-arm64
image: plugins/docker:linux-arm64
pull: true
settings:
@ -389,7 +389,7 @@ steps:
password:
from_secret: docker_password
repo: vikunja/frontend
tags: latest-linux-arm64
tags: unstable-linux-arm64
build_args:
- USE_RELEASE=true
- RELEASE_VERSION=unstable
@ -438,7 +438,7 @@ trigger:
- "refs/tags/**"
steps:
- name: docker-latest
- name: docker-unstable
image: plugins/docker:linux-amd64
pull: true
settings:
@ -447,7 +447,7 @@ steps:
password:
from_secret: docker_password
repo: vikunja/frontend
tags: latest-linux-amd64
tags: unstable-linux-amd64
build_args:
- USE_RELEASE=true
- RELEASE_VERSION=unstable
@ -488,12 +488,12 @@ depends_on:
- docker-arm-release
steps:
- name: manifest-latest
- name: manifest-unstable
pull: always
image: plugins/manifest
settings:
tags: latest
spec: docker-manifest-latest.tmpl
tags: unstable
spec: docker-manifest-unstable.tmpl
password:
from_secret: docker_password
username:
@ -516,6 +516,23 @@ 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
@ -555,8 +572,8 @@ kind: pipeline
type: docker
name: update-translations
#depends_on:
# - build
depends_on:
- build
trigger:
branch:
@ -577,9 +594,19 @@ steps:
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
image: appleboy/drone-git-push
depends_on:
- move-files
settings:
author_email: "frederik@vikunja.io"
author_name: Frederick [Bot]
@ -593,6 +620,8 @@ steps:
- name: upload
pull: always
image: jonasfranz/crowdin
depends_on:
- clone
settings:
files:
en.json: src/i18n/lang/en.json

View File

@ -2,13 +2,347 @@
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.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
@ -148,7 +482,8 @@ 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
@ -675,7 +1010,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
@ -710,7 +1045,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)
@ -1055,6 +1390,7 @@ 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

@ -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.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/)
[![Download](https://img.shields.io/badge/download-v0.18.1-brightgreen.svg)](https://dl.vikunja.io)
[![Translation](https://badges.crowdin.net/vikunja/localized.svg)](https://crowdin.com/project/vikunja)
This is the web frontend for Vikunja, written in Vue.js.

View File

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

View File

@ -14,7 +14,7 @@
"test:frontend": "cypress run"
},
"dependencies": {
"browserslist": "4.16.8",
"browserslist": "4.17.0",
"bulma": "0.9.3",
"camel-case": "4.1.2",
"copy-to-clipboard": "3.3.1",
@ -23,7 +23,7 @@
"highlight.js": "11.2.0",
"is-touch-device": "1.0.1",
"lodash": "4.17.21",
"marked": "3.0.2",
"marked": "3.0.3",
"register-service-worker": "1.7.2",
"snake-case": "3.0.4",
"verte": "0.0.12",
@ -35,44 +35,44 @@
"vue-shortkey": "3.1.7",
"vuedraggable": "2.24.3",
"vuex": "3.6.2",
"workbox-precaching": "6.2.4"
"workbox-precaching": "6.3.0"
},
"devDependencies": {
"@4tw/cypress-drag-drop": "1.8.1",
"@4tw/cypress-drag-drop": "2.0.0",
"@fortawesome/fontawesome-svg-core": "1.2.36",
"@fortawesome/free-regular-svg-icons": "5.15.4",
"@fortawesome/free-solid-svg-icons": "5.15.4",
"@fortawesome/vue-fontawesome": "2.0.2",
"@types/jest": "27.0.1",
"@typescript-eslint/eslint-plugin": "4.30.0",
"@typescript-eslint/parser": "4.30.0",
"@typescript-eslint/eslint-plugin": "4.31.0",
"@typescript-eslint/parser": "4.31.0",
"@vue/babel-preset-app": "4.5.13",
"@vue/eslint-config-typescript": "7.0.0",
"autoprefixer": "10.3.4",
"axios": "0.21.3",
"axios": "0.21.4",
"babel-eslint": "10.1.0",
"cypress": "8.3.1",
"cypress-file-upload": "5.0.8",
"esbuild": "0.12.25",
"esbuild": "0.12.26",
"eslint": "7.32.0",
"eslint-plugin-vue": "7.17.0",
"express": "4.17.1",
"faker": "5.5.3",
"jest": "27.1.0",
"jest": "27.1.1",
"rollup-plugin-terser": "7.0.2",
"rollup-plugin-visualizer": "5.5.2",
"sass": "1.39.0",
"sass": "1.39.2",
"ts-jest": "27.0.5",
"typescript": "4.4.2",
"vite": "2.5.3",
"vite": "2.5.6",
"vite-plugin-pwa": "0.11.2",
"vite-plugin-vue2": "1.8.1",
"vite-plugin-vue2": "1.8.2",
"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",
"workbox-cli": "6.2.4"
"workbox-cli": "6.3.0"
},
"eslintConfig": {
"root": true,

View File

@ -23,11 +23,9 @@
</template>
<script>
import {mapState} from 'vuex'
import {mapState, mapGetters} from 'vuex'
import isTouchDevice from 'is-touch-device'
import authTypes from './models/authTypes'
import Notification from './components/misc/notification'
import {KEYBOARD_SHORTCUTS_ACTIVE, ONLINE} from './store/mutation-types'
import KeyboardShortcuts from './components/misc/keyboard-shortcuts'
@ -74,11 +72,13 @@ export default {
return isTouchDevice()
},
...mapState({
authUser: state => state.auth.authenticated && (state.auth.info && state.auth.info.type === authTypes.USER),
authLinkShare: state => state.auth.authenticated && (state.auth.info && state.auth.info.type === authTypes.LINK_SHARE),
online: ONLINE,
keyboardShortcutsActive: KEYBOARD_SHORTCUTS_ACTIVE,
}),
...mapGetters('auth', [
'authUser',
'authLinkShare',
]),
},
methods: {
setupOnlineStatus() {

View File

@ -5,7 +5,7 @@
</a>
<div
:class="{'has-background': background}"
:style="{'background-image': `url(${background})`}"
:style="{'background-image': background && `url(${background})`}"
class="app-container"
>
<navigation/>
@ -62,7 +62,7 @@ export default {
return state.namespaces.namespaces.filter(n => !n.isArchived)
},
currentList: CURRENT_LIST,
background: 'background', // FIXME: Return the full thing or nothing at all to prevent calls to /null
background: 'background',
menuActive: MENU_ACTIVE,
userInfo: state => state.auth.info,
authenticated: state => state.auth.authenticated,

View File

@ -97,7 +97,7 @@
<script>
import {mapState} from 'vuex'
import {CURRENT_LIST, QUICK_ACTIONS_ACTIVE} from '@/store/mutation-types'
import Rights from '@/models/rights.json'
import Rights from '@/models/constants/rights.json'
import Update from '@/components/home/update.vue'
import ListSettingsDropdown from '@/components/list/list-settings-dropdown.vue'
import Dropdown from '@/components/misc/dropdown.vue'

View File

@ -4,7 +4,7 @@
:checked="checked"
:disabled="disabled"
:id="checkBoxId"
@change="updateData"
@change="(event) => updateData(event.target.checked)"
style="display: none;"
type="checkbox"/>
<label :for="checkBoxId" class="check">
@ -51,10 +51,10 @@ export default {
this.checkBoxId = 'fancycheckbox' + Math.random()
},
methods: {
updateData(e) {
this.checked = e.target.checked
this.$emit('input', this.checked)
this.$emit('change', e.target.checked)
updateData(checked) {
this.checked = checked
this.$emit('input', checked)
this.$emit('change', checked)
},
},
}

View File

@ -190,6 +190,35 @@ import ListService from '@/services/list'
import NamespaceService from '@/services/namespace'
import EditLabels from '@/components/tasks/partials/editLabels.vue'
// FIXME: merge with DEFAULT_PARAMS in taskList.js
const DEFAULT_PARAMS = {
sort_by: [],
order_by: [],
filter_by: [],
filter_value: [],
filter_comparator: [],
filter_include_nulls: true,
filter_concat: 'or',
s: '',
}
const DEFAULT_FILTERS = {
done: false,
dueDate: '',
requireAllFilters: false,
priority: 0,
usePriority: false,
startDate: '',
endDate: '',
percentDone: 0,
usePercentDone: false,
reminders: '',
assignees: '',
labels: '',
list_id: '',
namespace: '',
}
export default {
name: 'filters',
components: {
@ -202,32 +231,8 @@ export default {
},
data() {
return {
params: {
sort_by: [],
order_by: [],
filter_by: [],
filter_value: [],
filter_comparator: [],
filter_include_nulls: true,
filter_concat: 'or',
s: '',
},
filters: {
done: false,
dueDate: '',
requireAllFilters: false,
priority: 0,
usePriority: false,
startDate: '',
endDate: '',
percentDone: 0,
usePercentDone: false,
reminders: '',
assignees: '',
labels: '',
list_id: '',
namespace: '',
},
params: DEFAULT_PARAMS,
filters: DEFAULT_FILTERS,
usersService: UserService,
foundusers: [],

View File

@ -36,14 +36,7 @@
<div class="migration-in-progress">
<img :alt="name" :src="`/images/migration/${identifier}.png`"/>
<div class="progress-dots">
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span v-for="i in progressDotsCount" :key="i" />
</div>
<img alt="Vikunja" src="/images/logo.svg">
</div>
@ -74,15 +67,19 @@
import AbstractMigrationService from '../../services/migrator/abstractMigration'
import AbstractMigrationFileService from '../../services/migrator/abstractMigrationFile'
const PROGRESS_DOTS_COUNT = 8
export default {
name: 'migration',
data() {
return {
progressDotsCount: PROGRESS_DOTS_COUNT,
authUrl: '',
isMigrating: false,
lastMigrationDate: null,
message: '',
migratorAuthCode: '',
migrationService: null,
}
},
props: {

View File

@ -24,7 +24,7 @@
</div>
<div class="api-url-info" v-else>
<i18n path="apiConfig.signInOn">
<span class="url" v-tooltip="apiUrl"> {{ apiDomain() }} </span>
<span class="url" v-tooltip="apiUrl"> {{ apiDomain }} </span>
</i18n>
<br />
<a @click="() => (configureApi = true)">{{ $t('apiConfig.change') }}</a>
@ -46,23 +46,24 @@
</template>
<script>
const API_DEFAULT_PORT = 3456
export default {
name: 'apiConfig',
data() {
return {
configureApi: false,
apiUrl: '',
apiUrl: window.API_URL,
errorMsg: '',
successMsg: '',
}
},
created() {
this.apiUrl = window.API_URL
if (this.apiUrl === '') {
this.configureApi = true
}
},
methods: {
computed: {
apiDomain() {
if (window.API_URL.startsWith('/api/v1')) {
return window.location.host
@ -72,6 +73,8 @@ export default {
.split(/[/?#]/)
return urlParts[0]
},
},
methods: {
setApiUrl() {
if (this.apiUrl === '') {
return
@ -131,17 +134,17 @@ export default {
return Promise.reject(e)
})
.catch((e) => {
// Check if it is reachable at port 3456 and https
if (urlToCheck.port !== 3456) {
// Check if it is reachable at port API_DEFAULT_PORT and https
if (urlToCheck.port !== API_DEFAULT_PORT) {
urlToCheck.protocol = 'https:'
urlToCheck.port = 3456
urlToCheck.port = API_DEFAULT_PORT
window.API_URL = urlToCheck.toString()
return this.$store.dispatch('config/update')
}
return Promise.reject(e)
})
.catch((e) => {
// Check if it is reachable at :3456 and /api/v1 and https
// Check if it is reachable at :API_DEFAULT_PORT and /api/v1 and https
urlToCheck.pathname = origUrlToCheck.pathname
if (
!urlToCheck.pathname.endsWith('/api/v1') &&
@ -154,17 +157,17 @@ export default {
return Promise.reject(e)
})
.catch((e) => {
// Check if it is reachable at port 3456 and http
if (urlToCheck.port !== 3456) {
// Check if it is reachable at port API_DEFAULT_PORT and http
if (urlToCheck.port !== API_DEFAULT_PORT) {
urlToCheck.protocol = 'http:'
urlToCheck.port = 3456
urlToCheck.port = API_DEFAULT_PORT
window.API_URL = urlToCheck.toString()
return this.$store.dispatch('config/update')
}
return Promise.reject(e)
})
.catch((e) => {
// Check if it is reachable at :3456 and /api/v1 and http
// Check if it is reachable at :API_DEFAULT_PORT and /api/v1 and http
urlToCheck.pathname = origUrlToCheck.pathname
if (
!urlToCheck.pathname.endsWith('/api/v1') &&
@ -179,14 +182,14 @@ export default {
.catch(() => {
// Still not found, url is still invalid
this.successMsg = ''
this.errorMsg = this.$t('apiConfig.error', {domain: this.apiDomain()})
this.errorMsg = this.$t('apiConfig.error', {domain: this.apiDomain})
window.API_URL = oldUrl
})
.then((r) => {
if (typeof r !== 'undefined') {
// Set it + save it to local storage to save us the hoops
this.errorMsg = ''
this.successMsg = this.$t('apiConfig.success', {domain: this.apiDomain()})
this.successMsg = this.$t('apiConfig.success', {domain: this.apiDomain})
localStorage.setItem('API_URL', window.API_URL)
this.configureApi = false
this.apiUrl = window.API_URL

View File

@ -50,7 +50,7 @@
<script>
import NotificationService from '@/services/notification'
import User from '@/components/misc/user.vue'
import names from '@/models/notificationNames.json'
import names from '@/models/constants/notificationNames.json'
import {closeWhenClickedOutside} from '@/helpers/closeWhenClickedOutside'
import {mapState} from 'vuex'

View File

@ -173,7 +173,7 @@
</template>
<script>
import rights from '../../models/rights'
import rights from '../../models/constants/rights'
import LinkShareService from '../../services/linkShare'
import LinkShareModel from '../../models/linkShare'

View File

@ -145,7 +145,7 @@ import TeamListService from '../../services/teamList'
import TeamService from '../../services/team'
import TeamModel from '../../models/team'
import rights from '../../models/rights'
import rights from '../../models/constants/rights.json'
import Multiselect from '@/components/input/multiselect.vue'
import Nothing from '@/components/misc/nothing.vue'

View File

@ -72,7 +72,7 @@
import ListService from '../../services/list'
import TaskService from '../../services/task'
import TaskModel from '../../models/task'
import priorities from '../../models/priorities'
import priorities from '../../models/constants/priorities'
import EditLabels from './partials/editLabels'
import Reminders from './partials/reminders'
import ColorPicker from '../input/colorPicker'

View File

@ -11,7 +11,7 @@
</x-button>
</div>
<filter-popup
@change="loadTasks"
@change="loadTasks()"
:visible="showTaskFilter"
v-model="params"
/>
@ -24,10 +24,7 @@
class="month"
v-for="(m, mk) in days[yk]"
>
{{
new Date(new Date(`${yk}-${parseInt(mk) + 1}-01`)).toLocaleString('en-us', {month: 'long'})
}},
{{ new Date(yk).getFullYear() }}
{{ formatYear(new Date(`${yk}-${parseInt(mk) + 1}-01`)) }}
<div class="days">
<div
:class="{ today: d.toDateString() === now.toDateString() }"
@ -191,12 +188,13 @@ import EditTask from './edit-task'
import TaskService from '../../services/task'
import TaskModel from '../../models/task'
import priorities from '../../models/priorities'
import priorities from '../../models/constants/priorities'
import PriorityLabel from './partials/priorityLabel'
import TaskCollectionService from '../../services/taskCollection'
import {mapState} from 'vuex'
import Rights from '../../models/rights.json'
import Rights from '../../models/constants/rights.json'
import FilterPopup from '@/components/list/partials/filter-popup.vue'
import {format} from 'date-fns'
export default {
name: 'GanttChart',
@ -481,6 +479,9 @@ export default {
this.error(e)
})
},
formatYear(date) {
return format(date, 'MMMM, yyyy')
},
},
}
</script>

View File

@ -1,17 +1,70 @@
import TaskCollectionService from '../../../services/taskCollection'
import TaskCollectionService from '@/services/taskCollection'
import cloneDeep from 'lodash/cloneDeep'
import {calculateItemPosition} from '../../../helpers/calculateItemPosition'
// FIXME: merge with DEFAULT_PARAMS in filters.vue
const DEFAULT_PARAMS = {
sort_by: ['position', 'id'],
order_by: ['asc', 'desc'],
filter_by: ['done'],
filter_value: ['false'],
filter_comparator: ['equals'],
filter_concat: 'and',
}
function createPagination(totalPages, currentPage) {
const pages = []
for (let i = 0; i < totalPages; i++) {
// Show ellipsis instead of all pages
if (
i > 0 && // Always at least the first page
(i + 1) < totalPages && // And the last page
(
// And the current with current + 1 and current - 1
(i + 1) > currentPage + 1 ||
(i + 1) < currentPage - 1
)
) {
// Only add an ellipsis if the last page isn't already one
if (pages[i - 1] && !pages[i - 1].isEllipsis) {
pages.push({
number: 0,
isEllipsis: true,
})
}
continue
}
pages.push({
number: i + 1,
isEllipsis: false,
})
}
return pages
}
export function getRouteForPagination(page = 1, type = 'list') {
return {
name: 'list.' + type,
params: {
type: type,
},
query: {
page: page,
},
}
}
/**
* This mixin provides a base set of methods and properties to get tasks on a list.
*/
export default {
data() {
return {
taskCollectionService: TaskCollectionService,
taskCollectionService: new TaskCollectionService(),
tasks: [],
pages: [],
currentPage: 0,
loadedList: null,
@ -20,27 +73,21 @@ export default {
searchTerm: '',
showTaskFilter: false,
params: {
sort_by: ['position', 'id'],
order_by: ['asc', 'desc'],
filter_by: ['done'],
filter_value: ['false'],
filter_comparator: ['equals'],
filter_concat: 'and',
},
params: DEFAULT_PARAMS,
}
},
watch: {
'$route.query': 'loadTasksForPage', // Only listen for query path changes
// Only listen for query path changes
'$route.query': {
handler: 'loadTasksForPage',
immediate: true,
},
'$route.path': 'loadTasksOnSavedFilter',
},
beforeMount() {
// Triggering loading the tasks in beforeMount lets the component maintain the current page, therefore the page
// is not lost after navigating back from a task detail page for example.
this.loadTasksForPage(this.$route.query)
},
created() {
this.taskCollectionService = new TaskCollectionService()
computed: {
pages() {
return createPagination(this.taskCollectionService.totalPages, this.currentPage)
},
},
methods: {
loadTasks(
@ -80,48 +127,20 @@ export default {
return
}
this.$set(this, 'tasks', [])
this.tasks = []
this.taskCollectionService.getAll(list, params, page)
.then(r => {
this.$set(this, 'tasks', r)
this.$set(this, 'pages', [])
this.tasks = r
this.currentPage = page
for (let i = 0; i < this.taskCollectionService.totalPages; i++) {
// Show ellipsis instead of all pages
if (
i > 0 && // Always at least the first page
(i + 1) < this.taskCollectionService.totalPages && // And the last page
(
// And the current with current + 1 and current - 1
(i + 1) > this.currentPage + 1 ||
(i + 1) < this.currentPage - 1
)
) {
// Only add an ellipsis if the last page isn't already one
if (this.pages[i - 1] && !this.pages[i - 1].isEllipsis) {
this.pages.push({
number: 0,
isEllipsis: true,
})
}
continue
}
this.pages.push({
number: i + 1,
isEllipsis: false,
})
}
this.loadedList = cloneDeep(currentList)
})
.catch(e => {
this.error(e)
})
},
loadTasksForPage(e) {
// The page parameter can be undefined, in the case where the user loads a new list from the side bar menu
let page = Number(e.page)
@ -177,17 +196,6 @@ export default {
this.showTaskSearch = false
}, 200)
},
getRouteForPagination(page = 1, type = 'list') {
return {
name: 'list.' + type,
params: {
type: type,
},
query: {
page: page,
},
}
},
saveTaskPosition(e) {
this.drag = false
@ -205,5 +213,6 @@ export default {
this.error(e)
})
},
getRouteForPagination,
},
}

View File

@ -7,16 +7,17 @@
<h1
class="title input"
:class="{'disabled': !canWrite}"
@focusout="save()"
@keydown.enter.prevent.stop="save()"
@blur="save($event.target.textContent)"
@keydown.enter.prevent.stop="$event.target.blur()"
:contenteditable="canWrite ? 'true' : 'false'"
spellcheck="false"
ref="taskTitle">{{ task.title.trim() }}</h1>
<transition name="fade">
<span class="is-inline-flex is-align-items-center" v-if="loading && saving">
<span class="loader is-inline-block mr-2"></span>
{{ $t('misc.saving') }}
</span>
<span class="has-text-success is-inline-flex is-align-content-center" v-if="!loading && saved">
<span class="has-text-success is-inline-flex is-align-content-center" v-if="!loading && showSavedMessage">
<icon icon="check" class="mr-2"/>
{{ $t('misc.saved') }}
</span>
@ -25,22 +26,22 @@
</template>
<script>
import {LOADING} from '@/store/mutation-types'
import {mapState} from 'vuex'
export default {
name: 'heading',
data() {
return {
task: {title: '', identifier: '', index:''},
taskTitle: '',
saved: false,
showSavedMessage: false,
saving: false, // Since loading is global state, this variable ensures we're only showing the saving icon when saving the description.
}
},
computed: mapState({
loading: LOADING,
}),
computed: {
...mapState(['loading']),
task() {
return this.value
},
},
props: {
value: {
required: true,
@ -50,43 +51,29 @@ export default {
default: false,
},
},
watch: {
value(newVal) {
this.task = newVal
this.taskTitle = this.task.title
},
},
mounted() {
this.task = this.value
this.taskTitle = this.task.title
},
methods: {
save() {
this.$refs.taskTitle.spellcheck = false
// Pull the task title from the contenteditable
let taskTitle = this.$refs.taskTitle.textContent
this.task.title = taskTitle
// We only want to save if the title was actually change.
// Because the contenteditable does not have a change event,
// we're building it ourselves and only calling saveTask()
save(title) {
// We only want to save if the title was actually changed.
// Because the contenteditable does not have a change event
// we're building it ourselves and only continue
// if the task title changed.
if (this.task.title !== this.taskTitle) {
this.$refs.taskTitle.blur()
this.saveTask()
this.taskTitle = taskTitle
if (title === this.task.title) {
return
}
},
saveTask() {
this.saving = true
this.$store.dispatch('tasks/update', this.task)
.then(() => {
this.$emit('input', this.task)
this.saved = true
const newTask = {
...this.task,
title,
}
this.$store.dispatch('tasks/update', newTask)
.then((task) => {
this.$emit('input', task)
this.showSavedMessage = true
setTimeout(() => {
this.saved = false
this.showSavedMessage = false
}, 2000)
})
.catch(e => {

View File

@ -92,8 +92,10 @@ export default {
methods: {
markTaskAsDone(task) {
this.loadingInternal = true
task.done = !task.done
this.$store.dispatch('tasks/update', task)
this.$store.dispatch('tasks/update', {
...task,
done: !task.done,
})
.then(() => {
if (task.done) {
playPop()

View File

@ -21,7 +21,7 @@
</template>
<script>
import priorites from '../../../models/priorities'
import priorites from '../../../models/constants/priorities'
export default {
name: 'priorityLabel',

View File

@ -12,7 +12,7 @@
</template>
<script>
import priorites from '../../../models/priorities'
import priorites from '../../../models/constants/priorities'
export default {
name: 'prioritySelect',

View File

@ -122,7 +122,7 @@
import TaskService from '../../../services/task'
import TaskModel from '../../../models/task'
import TaskRelationService from '../../../services/taskRelation'
import relationKinds from '../../../models/relationKinds'
import relationKinds from '../../../models/constants/relationKinds'
import TaskRelationModel from '../../../models/taskRelation'
import Multiselect from '@/components/input/multiselect.vue'

View File

@ -51,7 +51,7 @@
</template>
<script>
import repeatModes from '@/models/taskRepeatModes'
import repeatModes from '@/models/constants/taskRepeatModes'
export default {
name: 'repeatAfter',
@ -62,7 +62,7 @@ export default {
amount: 0,
type: '',
},
repeatModes: repeatModes,
repeatModes,
}
},
props: {

3
src/helpers/find.ts Normal file
View File

@ -0,0 +1,3 @@
export function findIndexById(array : [], id : string | number) {
return array.findIndex(({id: currentId}) => currentId === id)
}

View File

@ -113,11 +113,12 @@
"scheduledCancelSuccess": "Wir werden deinen Account nicht löschen."
},
"export": {
"title": "Exportiere die Daten deines Vikunja-Accounts",
"title": "Export your Vikunja data",
"description": "Du kannst eine Kopie deiner Daten bei Vikunja anfordern. Dazu gehören Namespaces, Listen, Aufgaben und alles, was damit zusammenhängt. Du kannst diese Daten dann in jeder Vikunja-Instanz über die Migrationsfunktion importieren.",
"descriptionPasswordRequired": "Bitte gib dein Passwort ein, um fortzufahren:",
"request": "Eine Kopie meiner Vikunja Daten anfordern",
"success": "Du hast deine Daten bei Vikunja erfolgreich angefordert! Wir schicken dir eine E-Mail, sobald sie zum Download bereitstehen."
"success": "Du hast deine Daten bei Vikunja erfolgreich angefordert! Wir schicken dir eine E-Mail, sobald sie zum Download bereitstehen.",
"downloadTitle": "Download your exported Vikunja data"
}
},
"list": {

View File

@ -113,11 +113,12 @@
"scheduledCancelSuccess": "Wir werden deinen Account nicht löschen."
},
"export": {
"title": "Exportiere die Daten deines Vikunja-Accounts",
"title": "Export your Vikunja data",
"description": "Du kannst eine Kopie deiner Daten bei Vikunja anfordern. Dazu gehören Namespaces, Listen, Aufgaben und alles, was damit zusammenhängt. Du kannst diese Daten dann in jeder Vikunja-Instanz über die Migrationsfunktion importieren.",
"descriptionPasswordRequired": "Bitte gib dein Passwort ein, um fortzufahren:",
"request": "Eine Kopie meiner Vikunja Daten anfordern",
"success": "Du hast deine Daten bei Vikunja erfolgreich angefordert! Wir schicken dir eine E-Mail, sobald sie zum Download bereitstehen."
"success": "Du hast deine Daten bei Vikunja erfolgreich angefordert! Wir schicken dir eine E-Mail, sobald sie zum Download bereitstehen.",
"downloadTitle": "Download your exported Vikunja data"
}
},
"list": {

View File

@ -1,309 +0,0 @@
{
"404": {
"title": "Nicht gefunden",
"text": "Die angeforderte Seite existiert nicht."
},
"filters": {
"create": {
"action": "Neuen gespeicherten Filter erstellen",
"description": "Ein gespeicherter Filter ist eine virtuelle Liste, die bei jedem Zugriff aus einem Satz von Filtern errechnet wird. Einmal erstellt, erscheint er in einem speziellen Namensraum.",
"title": "Einen gespeicherten Filter erstellen"
},
"attributes": {
"descriptionPlaceholder": "Die Beschreibung steht hier …",
"description": "Beschreibung",
"titlePlaceholder": "Der gespeicherte Filtertitel steht hier …",
"title": "Titel"
},
"delete": {
"header": "Diesen gespeicherten Filter löschen",
"success": "Der Filter wurde erfolgreich gelöscht."
},
"edit": {
"success": "Der Filter wurde erfolgreich gespeichert.",
"title": "Diesen gespeicherten Filter bearbeiten"
},
"title": "Filter"
},
"sharing": {
"authenticating": "Authentifizierung …",
"invalidPassword": "Das Passwort ist ungültig.",
"error": "Es ist ein Fehler aufgetreten."
},
"label": {
"attributes": {
"color": "Farbe",
"description": "Beschreibung",
"title": "Titel"
}
},
"misc": {
"search": "Suchen",
"copy": "In Zwischenablage kopieren",
"disable": "Deaktivieren",
"confirm": "Bestätigen",
"delete": "Löschen",
"save": "Speichern",
"loading": "Wird geladen …",
"previous": "Vorherige",
"next": "Weiter"
},
"task": {
"delete": "Diese Aufgabe löschen",
"new": "Eine neue Aufgabe erstellen",
"task": "Aufgabe",
"show": {
"titleCurrent": "Aktuelle Aufgaben",
"noTasks": "Nichts zu tun Einen schönen Tag noch!",
"today": "Heute",
"nextWeek": "Nächste Woche"
},
"detail": {
"created": "Erstellt {0} von {1}",
"undone": "Als unerledigt markieren",
"done": "Fertig!",
"move": "Aufgabe in eine andere Liste verschieben",
"delete": {
"header": "Diese Aufgabe löschen"
},
"deleteSuccess": "Die Aufgabe wurde erfolgreich gelöscht.",
"updateSuccess": "Die Aufgabe wurde erfolgreich gespeichert.",
"doneAt": "Erledigt {0}",
"updated": "Aktualisiert {0}",
"actions": {
"priority": "Priorität einstellen",
"reminders": "Erinnerungen einstellen",
"relatedTasks": "Aufgabenbeziehungen hinzufügen",
"attachments": "Anhänge hinzufügen",
"delete": "Aufgabe löschen",
"color": "Taskfarbe einstellen",
"moveList": "Aufgabe verschieben"
}
},
"attributes": {
"color": "Farbe",
"done": "Fertig",
"createdBy": "Erstellt von",
"created": "Erstellt",
"endDate": "Enddatum",
"dueDate": "Fälligkeitsdatum",
"title": "Titel",
"startDate": "Anfangsdatum",
"relatedTasks": "Verwandte Aufgaben",
"priority": "Priorität",
"percentDone": "% erledigt",
"repeat": "Wiederholen",
"reminders": "Erinnerungen",
"updated": "Aktualisiert"
}
},
"team": {
"edit": {
"delete": {
"header": "Team löschen",
"success": "Das Team wurde erfolgreich gelöscht."
},
"madeAdmin": "Das Teammitglied wurde erfolgreich zum Admin gemacht.",
"madeMember": "Das Teammitglied wurde erfolgreich zum Mitglied gemacht.",
"userAddedSuccess": "Das Teammitglied wurde erfolgreich hinzugefügt.",
"success": "Das Team wurde erfolgreich aktualisiert.",
"addUser": "Zum Team hinzufügen",
"members": "Teammitglieder",
"title": "Team „{team}“ bearbeiten",
"deleteUser": {
"header": "Benutzer aus dem Team entfernen"
}
},
"create": {
"success": "Das Team wurde erfolgreich erstellt.",
"title": "Ein neues Team erstellen"
},
"title": "Teams",
"attributes": {
"description": "Beschreibung",
"descriptionPlaceholder": "Die Beschreibung des Teams steht hier …",
"member": "Mitglied",
"admin": "Admin",
"name": "Teamname",
"namePlaceholder": "Der Name des Teams steht hier …"
}
},
"namespace": {
"create": {
"explanation": "Ein Namensraum ist eine Sammlung von Listen, die man teilen und verwenden kann, um seine Listen zu organisieren. Tatsächlich gehört jede Liste zu einem Namensraum.",
"success": "Der Namensraum wurde erfolgreich angelegt.",
"tooltip": "Was ist ein Namensraum?",
"title": "Einen neuen Namensraum erstellen"
},
"attributes": {
"isArchived": "Dieser Namensraum wird archiviert",
"archived": "Ist archiviert",
"color": "Farbe",
"descriptionPlaceholder": "Die Beschreibung des Namensraums steht hier …",
"description": "Beschreibung",
"titlePlaceholder": "Der Titel des Namensraums steht hier …",
"title": "Namensraumtitel"
},
"share": {
"title": "„{namespace}“ teilen"
},
"edit": {
"success": "Der Namensraum wurde erfolgreich aktualisiert.",
"title": "„{namespace}“ bearbeiten"
},
"delete": {
"success": "Der Namensraum wurde erfolgreich gelöscht.",
"text2": "Dies umfasst alle Listen und Aufgaben und kann NICHT rückgängig gemacht werden!",
"title": "„{namespace}“ löschen"
},
"archive": {
"description": "Wenn ein Namensraum archiviert ist, kann man keine neuen Listen erstellen oder ihn bearbeiten.",
"success": "Der Namensraum wurde erfolgreich archiviert.",
"titleUnarchive": "Archivierung von „{namespace}“ aufheben",
"titleArchive": "„{namespace}“ archivieren"
},
"noLists": "Dieser Namensraum enthält keine Listen.",
"title": "Namensräume & Listen",
"unarchive": "Archivierung aufheben",
"archived": "Archiviert",
"showArchived": "Archivierte anzeigen"
},
"list": {
"kanban": {
"bucketTitleSavedSuccess": "Der Eimertitel wurde erfolgreich gespeichert.",
"deleteBucketSuccess": "Der Eimer wurde erfolgreich gelöscht.",
"deleteBucketText2": "Dies löscht keine Aufgaben, sondern verschiebt sie in den Standard-Eimer.",
"deleteHeaderBucket": "Den Eimer löschen",
"addBucket": "Einen neuen Eimer erstellen",
"addAnotherTask": "Weitere Aufgabe hinzufügen",
"addTask": "Eine Aufgabe hinzufügen",
"doneBucket": "Erledigte-Dinge-Eimer",
"noLimit": "Nicht eingestellt"
},
"table": {
"columns": "Spalten",
"title": "Tabelle"
},
"gantt": {
"to": "An",
"from": "Von",
"day": "Tag",
"month": "Monat",
"default": "Standard",
"size": "Größe",
"showTasksWithoutDates": "Aufgaben anzeigen, für die keine Termine festgelegt sind",
"title": "Gantt"
},
"list": {
"empty": "Diese Liste ist derzeit leer.",
"addPlaceholder": "Eine neue Aufgabe hinzufügen …",
"add": "Hinzufügen",
"title": "Liste"
},
"share": {
"title": "„{Liste}“ teilen",
"header": "Diese Liste teilen"
},
"edit": {
"success": "Die Liste wurde erfolgreich aktualisiert.",
"color": "Farbe",
"descriptionPlaceholder": "Die Listenbeschreibung geht hier …",
"description": "Beschreibung",
"identifierPlaceholder": "Der Listenbezeichner geht hier …",
"identifier": "Listebezeichner",
"identifierTooltip": "Der Listenbezeichner kann zur eindeutigen Identifizierung einer Aufgabe über Listen hinweg verwendet werden. Man kann ihn auf leer setzen, um ihn zu deaktivieren.",
"titlePlaceholder": "Der Titel der Liste steht hier …",
"title": "„{list}“ bearbeiten",
"header": "Diese Liste bearbeiten"
},
"duplicate": {
"success": "Die Liste wurde erfolgreich dupliziert.",
"label": "Duplizieren",
"title": "Diese Liste duplizieren"
},
"delete": {
"success": "Die Liste wurde erfolgreich gelöscht.",
"text2": "Dies umfasst alle Aufgaben und kann NICHT rückgängig gemacht werden!",
"header": "Diese Liste löschen",
"title": "„{list}“ löschen"
},
"background": {
"removeSuccess": "Der Hintergrund ist erfolgreich entfernt worden!",
"success": "Der Hintergrund ist erfolgreich eingestellt worden!",
"loadMore": "Mehr Fotos laden",
"poweredByUnsplash": "Angetrieben von Unsplash",
"searchPlaceholder": "Nach einem Hintergrund suchen …",
"remove": "Hintergrund entfernen",
"title": "Listenhintergrund festlegen"
},
"archive": {
"success": "Die Liste wurde erfolgreich archiviert.",
"unarchive": "Archivierung dieser Liste aufheben",
"archive": "Diese Liste archivieren",
"title": "„{Liste}“ archivieren"
},
"create": {
"createdSuccess": "Die Liste wurde erfolgreich erstellt.",
"titlePlaceholder": "Der Titel der Liste steht hier …",
"header": "Eine neue Liste erstellen"
},
"color": "Farbe"
},
"user": {
"settings": {
"caldav": {
"title": "Caldav"
},
"totp": {
"disableSuccess": "Die Zwei-Faktor-Authentifizierung wurde erfolgreich deaktiviert.",
"passcode": "Passcode",
"enroll": "Einschreiben",
"title": "Zwei-Faktor-Authentifizierung"
},
"general": {
"weekStartMonday": "Montag",
"weekStartSunday": "Sonntag",
"weekStart": "Woche beginnt am",
"playSoundWhenDone": "Einen Ton abspielen, wenn Aufgaben als erledigt markiert werden",
"discoverableByEmail": "Andere Benutzer mich finden lassen, wenn sie nach meiner vollständigen E-Mail suchen",
"discoverableByName": "Andere Benutzer mich finden lassen, wenn sie nach meinem Namen suchen",
"overdueReminders": "Mir jeden Morgen Erinnerungen für überfällige unerledigte Aufgaben per E-Mail senden",
"emailReminders": "Mir Erinnerungen für Aufgaben per E-Mail senden",
"savedSuccess": "Die Einstellungen wurden erfolgreich aktualisiert.",
"newName": "Der neue Name",
"name": "Name",
"title": "Allgemeine Einstellungen"
},
"updateEmailNew": "Neue E-Mail-Adresse",
"passwordUpdateSuccess": "Das Passwort wurde erfolgreich aktualisiert.",
"passwordsDontMatch": "Das neue Passwort und seine Bestätigung stimmen nicht überein.",
"currentPassword": "Aktuelles Passwort",
"newPasswordConfirm": "Neue Passwortbestätigung",
"newPassword": "Neues Passwort",
"title": "Einstellungen"
},
"auth": {
"openIdStateError": "Zustand stimmt nicht überein, weigert sich fortzufahren!",
"authenticating": "Authentifizierung …",
"loginWith": "Mit {provider} anmelden",
"register": "Registrieren",
"login": "Anmelden",
"totpPlaceholder": "z.B. 123456",
"totpTitle": "Zwei-Faktor-Authentifizierungscode",
"passwordsDontMatch": "Passwörter stimmen nicht überein",
"passwordPlaceholder": "z.B. •••••••••••",
"password": "Passwort",
"emailPlaceholder": "z.B. frederic@vikunja.io",
"email": "E-Mail-Adresse",
"usernamePlaceholder": "z.B. frederick",
"usernameEmail": "Benutzername oder E-Mail-Adresse",
"username": "Benutzername"
}
},
"home": {
"list": {
"new": "Eine neue Liste erstellen"
},
"welcome": "Hallo {username}"
}
}

View File

@ -113,11 +113,12 @@
"scheduledCancelSuccess": "We will not delete your account."
},
"export": {
"title": "Export your Vikunja Data",
"title": "Export your Vikunja data",
"description": "You can request a copy of all your Vikunja data. This include Namespaces, Lists, Tasks and everything associated to them. You can import this data in any Vikunja instance through the migration function.",
"descriptionPasswordRequired": "Please enter your password to proceed:",
"request": "Request a copy of my Vikunja Data",
"success": "You've successfully requested your Vikunja Data! We will send you an email once it's ready to download."
"success": "You've successfully requested your Vikunja Data! We will send you an email once it's ready to download.",
"downloadTitle": "Download your exported Vikunja data"
}
},
"list": {

View File

@ -113,11 +113,12 @@
"scheduledCancelSuccess": "We will not delete your account."
},
"export": {
"title": "Export your Vikunja Data",
"title": "Export your Vikunja data",
"description": "You can request a copy of all your Vikunja data. This include Namespaces, Lists, Tasks and everything associated to them. You can import this data in any Vikunja instance through the migration function.",
"descriptionPasswordRequired": "Please enter your password to proceed:",
"request": "Request a copy of my Vikunja Data",
"success": "You've successfully requested your Vikunja Data! We will send you an email once it's ready to download."
"success": "You've successfully requested your Vikunja Data! We will send you an email once it's ready to download.",
"downloadTitle": "Download your exported Vikunja data"
}
},
"list": {

View File

@ -1 +0,0 @@
{}

View File

@ -113,11 +113,12 @@
"scheduledCancelSuccess": "We will not delete your account."
},
"export": {
"title": "Export your Vikunja Data",
"title": "Export your Vikunja data",
"description": "You can request a copy of all your Vikunja data. This include Namespaces, Lists, Tasks and everything associated to them. You can import this data in any Vikunja instance through the migration function.",
"descriptionPasswordRequired": "Please enter your password to proceed:",
"request": "Request a copy of my Vikunja Data",
"success": "You've successfully requested your Vikunja Data! We will send you an email once it's ready to download."
"success": "You've successfully requested your Vikunja Data! We will send you an email once it's ready to download.",
"downloadTitle": "Download your exported Vikunja data"
}
},
"list": {

View File

@ -1 +0,0 @@
{}

View File

@ -113,11 +113,12 @@
"scheduledCancelSuccess": "We will not delete your account."
},
"export": {
"title": "Export your Vikunja Data",
"title": "Export your Vikunja data",
"description": "You can request a copy of all your Vikunja data. This include Namespaces, Lists, Tasks and everything associated to them. You can import this data in any Vikunja instance through the migration function.",
"descriptionPasswordRequired": "Please enter your password to proceed:",
"request": "Request a copy of my Vikunja Data",
"success": "You've successfully requested your Vikunja Data! We will send you an email once it's ready to download."
"success": "You've successfully requested your Vikunja Data! We will send you an email once it's ready to download.",
"downloadTitle": "Download your exported Vikunja data"
}
},
"list": {

View File

@ -1,34 +1,34 @@
{
"home": {
"welcomeNight": "Boa noite {username}",
"welcomeMorning": "Bom dia {username}",
"welcomeDay": "Olá {username}",
"welcomeEvening": "Boa tarde {username}",
"lastViewed": "Recentes",
"welcomeNight": "Good Night {username}",
"welcomeMorning": "Good Morning {username}",
"welcomeDay": "Hi {username}",
"welcomeEvening": "Good Evening {username}",
"lastViewed": "Last viewed",
"list": {
"newText": "Você pode criar uma nova lista para suas novas tarefas:",
"new": "Criar uma nova lista",
"importText": "Ou importe suas listas e tarefas de outros serviços no Vikunja:",
"import": "Importe seus dados para o Vikunja"
"newText": "You can create a new list for your new tasks:",
"new": "Create a new list",
"importText": "Or import your lists and tasks from other services into Vikunja:",
"import": "Import your data into Vikunja"
}
},
"404": {
"title": "Não encontrado",
"text": "A página solicitada não existe."
"title": "Not found",
"text": "The page you requested does not exist."
},
"user": {
"auth": {
"username": "Usuário",
"usernameEmail": "Usuário ou Email",
"usernamePlaceholder": "ex: lucas",
"email": "Endereço de e-mail",
"emailPlaceholder": "ex: lucas@vikunja.io",
"password": "Senha",
"passwordRepeat": "Digite novamente sua senha",
"passwordPlaceholder": "ex.: •••••••••••••",
"resetPassword": "Redefinir sua senha",
"resetPasswordAction": "Envie-me um link para redefinição de senha",
"resetPasswordSuccess": "Verifique sua caixa de entrada! Você deve ter um e-mail com instruções sobre como redefinir sua senha.",
"username": "Username",
"usernameEmail": "Username Or Email Address",
"usernamePlaceholder": "e.g. frederick",
"email": "E-mail address",
"emailPlaceholder": "e.g. frederic@vikunja.io",
"password": "Password",
"passwordRepeat": "Retype your password",
"passwordPlaceholder": "e.g. •••••••••••",
"resetPassword": "Reset your password",
"resetPasswordAction": "Send me a password reset link",
"resetPasswordSuccess": "Check your inbox! You should have an e-mail with instructions on how to reset your password.",
"passwordsDontMatch": "Passwords don't match",
"confirmEmailSuccess": "You successfully confirmed your email! You can log in now.",
"totpTitle": "Two Factor Authentication Code",
@ -113,11 +113,12 @@
"scheduledCancelSuccess": "We will not delete your account."
},
"export": {
"title": "Export your Vikunja Data",
"title": "Export your Vikunja data",
"description": "You can request a copy of all your Vikunja data. This include Namespaces, Lists, Tasks and everything associated to them. You can import this data in any Vikunja instance through the migration function.",
"descriptionPasswordRequired": "Please enter your password to proceed:",
"request": "Request a copy of my Vikunja Data",
"success": "You've successfully requested your Vikunja Data! We will send you an email once it's ready to download."
"success": "You've successfully requested your Vikunja Data! We will send you an email once it's ready to download.",
"downloadTitle": "Download your exported Vikunja data"
}
},
"list": {

View File

@ -1,101 +1,101 @@
{
"home": {
"welcomeNight": "Buenas noches {username}",
"welcomeMorning": "Buenos días {username}",
"welcomeDay": "Hola {username}",
"welcomeEvening": "Buenas tardes {username}",
"lastViewed": "Visto por última vez",
"welcomeNight": "Good Night {username}",
"welcomeMorning": "Good Morning {username}",
"welcomeDay": "Hi {username}",
"welcomeEvening": "Good Evening {username}",
"lastViewed": "Last viewed",
"list": {
"newText": "Puedes crear una nueva lista para las tareas nuevas:",
"new": "Crear una lista nueva",
"importText": "O importa tus listas y tareas de otros servicios a Vikunja:",
"import": "Importa tus datos a Vikunja"
"newText": "You can create a new list for your new tasks:",
"new": "Create a new list",
"importText": "Or import your lists and tasks from other services into Vikunja:",
"import": "Import your data into Vikunja"
}
},
"404": {
"title": "No encontrado",
"text": "La página solicitada no existe."
"title": "Not found",
"text": "The page you requested does not exist."
},
"user": {
"auth": {
"username": "Nombre de usuario",
"usernameEmail": "Nombre de usuario o dirección de correo electrónico",
"usernamePlaceholder": "p/ej. Federico",
"email": "Correo electrónico",
"emailPlaceholder": "p/ej. frederic@vikunja.io",
"password": "Contraseña",
"passwordRepeat": "Reescribe tu contraseña",
"passwordPlaceholder": "p/ej. •••••••••••",
"resetPassword": "Restablecer tu contraseña",
"resetPasswordAction": "Envíame un enlace para restablecer la contraseña",
"resetPasswordSuccess": "¡Revisa tu bandeja de entrada! Debes tener un correo electrónico con instrucciones para restablecer tu contraseña.",
"passwordsDontMatch": "Las contraseñas no coinciden",
"confirmEmailSuccess": "Has confirmado correctamente tu correo electrónico. Ya puedes conectarte.",
"totpTitle": "Código de autenticación de dos factores",
"totpPlaceholder": "p/ej. 123456",
"login": "Ingresar",
"register": "Registrarse",
"loginWith": "Inicie sesión con {provider}",
"authenticating": "Autenticando…",
"openIdStateError": "¡El estado no coincide, negándome a continuar!",
"username": "Username",
"usernameEmail": "Username Or Email Address",
"usernamePlaceholder": "e.g. frederick",
"email": "E-mail address",
"emailPlaceholder": "e.g. frederic@vikunja.io",
"password": "Password",
"passwordRepeat": "Retype your password",
"passwordPlaceholder": "e.g. •••••••••••",
"resetPassword": "Reset your password",
"resetPasswordAction": "Send me a password reset link",
"resetPasswordSuccess": "Check your inbox! You should have an e-mail with instructions on how to reset your password.",
"passwordsDontMatch": "Passwords don't match",
"confirmEmailSuccess": "You successfully confirmed your email! You can log in now.",
"totpTitle": "Two Factor Authentication Code",
"totpPlaceholder": "e.g. 123456",
"login": "Login",
"register": "Register",
"loginWith": "Log in with {provider}",
"authenticating": "Authenticating…",
"openIdStateError": "State does not match, refusing to continue!",
"openIdGeneralError": "An error occured while authenticating against the third party.",
"logout": "Cerrar sesión"
"logout": "Logout"
},
"settings": {
"title": "Opciones",
"newPasswordTitle": "Actualiza tu contraseña",
"newPassword": "Nueva contraseña",
"newPasswordConfirm": "Confirmar contraseña",
"currentPassword": "Contraseña actual",
"currentPasswordPlaceholder": "Tu contraseña actual",
"passwordsDontMatch": "La contraseña nueva y su confirmación no emparejan.",
"passwordUpdateSuccess": "La contraseña se actualizó correctamente.",
"updateEmailTitle": "Actualiza tu dirección de correo electrónico",
"updateEmailNew": "Nueva dirección de correo electrónico",
"updateEmailSuccess": "Dirección de correo electrónico actualizada. Haga clic en el enlace del correo electrónico que se te ha enviado para confirmarlo.",
"title": "Settings",
"newPasswordTitle": "Update Your Password",
"newPassword": "New Password",
"newPasswordConfirm": "New Password Confirmation",
"currentPassword": "Current Password",
"currentPasswordPlaceholder": "Your current password",
"passwordsDontMatch": "The new password and its confirmation don't match.",
"passwordUpdateSuccess": "The password was successfully updated.",
"updateEmailTitle": "Update Your E-Mail Address",
"updateEmailNew": "New Email Address",
"updateEmailSuccess": "Your email address was successfully updated. We've sent you a link to confirm it.",
"general": {
"title": "Configuración General",
"name": "Nombre",
"newName": "El nombre nuevo",
"savedSuccess": "Configuración actualizada.",
"emailReminders": "Enviarme recordatorios para tareas por correo electrónico",
"overdueReminders": "Enviarme recordatorios de tareas pendientes atrasadas por correo cada mañana",
"discoverableByName": "Permitir que otros usuarios me encuentren cuando busquen mi nombre",
"discoverableByEmail": "Permitir que otros usuarios me encuentren cuando busquen mi correo electrónico completo",
"playSoundWhenDone": "Reproducir un sonido cuando marcas tareas como hechas",
"weekStart": "La semana empieza en",
"weekStartSunday": "domingo",
"weekStartMonday": "lunes",
"language": "Idioma",
"defaultList": "Lista predeterminada"
"title": "General Settings",
"name": "Name",
"newName": "The new Name",
"savedSuccess": "The settings were successfully updated.",
"emailReminders": "Send me reminders for tasks via Email",
"overdueReminders": "Send me reminders for overdue undone tasks via email each morning",
"discoverableByName": "Let other users find me when they search for my name",
"discoverableByEmail": "Let other users find me when they search for my full email",
"playSoundWhenDone": "Play a sound when marking tasks as done",
"weekStart": "Week starts on",
"weekStartSunday": "Sunday",
"weekStartMonday": "Monday",
"language": "Language",
"defaultList": "Default List"
},
"totp": {
"title": "Autenticación de dos factores",
"enroll": "Inscribirse",
"finishSetupPart1": "Para finalizar tu configuración, utiliza este secreto en tu aplicación totp (Google Authenticator o similar):",
"finishSetupPart2": "Después, introduce un código de tu aplicación abajo.",
"scanQR": "Alternativamente, escanea este código QR:",
"passcode": "Código de acceso",
"passcodePlaceholder": "Un código generado por tu aplicación totp",
"setupSuccess": "¡Has configurado con éxito la autenticación de dos factores!",
"enterPassword": "Por favor, introduce tu contraseña",
"disable": "Desactivar la autenticación en dos pasos",
"confirmSuccess": "¡Has confirmado con éxito tu configuración totp y puedes usarla a partir de ahora!",
"disableSuccess": "La autenticación de dos factores se desactivó correctamente."
"title": "Two Factor Authentication",
"enroll": "Enroll",
"finishSetupPart1": "To finish your setup, use this secret in your totp app (Google Authenticator or similar):",
"finishSetupPart2": "After that, enter a code from your app below.",
"scanQR": "Alternatively you can scan this QR code:",
"passcode": "Passcode",
"passcodePlaceholder": "A code generated by your totp application",
"setupSuccess": "You've sucessfully set up two factor authentication!",
"enterPassword": "Please Enter Your Password",
"disable": "Disable two factor authentication",
"confirmSuccess": "You've successfully confirmed your totp setup and can use it from now on!",
"disableSuccess": "Two factor authentication was sucessfully disabled."
},
"caldav": {
"title": "Caldav",
"howTo": "Puedes conectar Vikunja a los clientes caldav para ver y gestionar todas las tareas desde diferentes clientes. Introduce esta url en tu cliente:",
"more": "Más información sobre caldav en Vikunja"
"howTo": "You can connect Vikunja to caldav clients to view and manage all tasks from different clients. Enter this url into your client:",
"more": "More information about caldav in Vikunja"
},
"avatar": {
"title": "Avatar",
"initials": "Iniciales",
"initials": "Initials",
"gravatar": "Gravatar",
"upload": "Subir",
"uploadAvatar": "Subir Avatar",
"statusUpdateSuccess": "¡El estado del avatar se ha actualizado correctamente!",
"setSuccess": "¡El avatar se ha establecido correctamente!"
"upload": "Upload",
"uploadAvatar": "Upload Avatar",
"statusUpdateSuccess": "Avatar status was updated successfully!",
"setSuccess": "The avatar has been set successfully!"
}
},
"deletion": {
@ -113,152 +113,153 @@
"scheduledCancelSuccess": "We will not delete your account."
},
"export": {
"title": "Export your Vikunja Data",
"title": "Export your Vikunja data",
"description": "You can request a copy of all your Vikunja data. This include Namespaces, Lists, Tasks and everything associated to them. You can import this data in any Vikunja instance through the migration function.",
"descriptionPasswordRequired": "Please enter your password to proceed:",
"request": "Request a copy of my Vikunja Data",
"success": "You've successfully requested your Vikunja Data! We will send you an email once it's ready to download."
"success": "You've successfully requested your Vikunja Data! We will send you an email once it's ready to download.",
"downloadTitle": "Download your exported Vikunja data"
}
},
"list": {
"archived": "Esta lista está archivada. No es posible crear nuevas o editar tareas o ello.",
"title": "Título de Lista",
"archived": "This list is archived. It is not possible to create new or edit tasks or it.",
"title": "List Title",
"color": "Color",
"lists": "Listas",
"search": "Escribe para buscar una lista…",
"searchSelect": "Haga clic o presione enter para seleccionar esta lista",
"shared": "Listas compartidas",
"lists": "Lists",
"search": "Type to search for a list…",
"searchSelect": "Click or press enter to select this list",
"shared": "Shared Lists",
"create": {
"header": "Crear una nueva lista",
"titlePlaceholder": "El título de la lista va aquí…",
"addTitleRequired": "Por favor, especifica un título.",
"createdSuccess": "La lista se ha creado correctamente.",
"addListRequired": "Por favor, especifique una lista o establezca una lista por defecto en la configuración."
"header": "Create a new list",
"titlePlaceholder": "The list's title goes here…",
"addTitleRequired": "Please specify a title.",
"createdSuccess": "The list was successfully created.",
"addListRequired": "Please specify a list or set a default list in the settings."
},
"archive": {
"title": "Archivar \"{list}\"",
"archive": "Archivar esta lista",
"unarchive": "Desarchivar esta lista",
"unarchiveText": "Podrás crear tareas nuevas o editarlas.",
"archiveText": "No podrás editar esta lista ni crear nuevas tareas hasta que la des-archives.",
"success": "La lista fue archivada exitosamente."
"title": "Archive \"{list}\"",
"archive": "Archive this list",
"unarchive": "Un-Archive this list",
"unarchiveText": "You will be able to create new tasks or edit it.",
"archiveText": "You won't be able to edit this list or create new tasks until you un-archive it.",
"success": "The list was successfully archived."
},
"background": {
"title": "Establecer el fondo de la lista",
"remove": "Eliminar fondo",
"upload": "Elige un fondo de tu pc",
"searchPlaceholder": "Buscar un fondo…",
"poweredByUnsplash": "Con tecnología de Unsplash",
"loadMore": "Cargar más fotos",
"success": "¡El fondo se ha establecido correctamente!",
"removeSuccess": "¡El fondo se ha eliminado exitosamente!"
"title": "Set list background",
"remove": "Remove Background",
"upload": "Choose a background from your pc",
"searchPlaceholder": "Search for a background…",
"poweredByUnsplash": "Powered by Unsplash",
"loadMore": "Load more photos",
"success": "The background has been set successfully!",
"removeSuccess": "The background has been removed successfully!"
},
"delete": {
"title": "Eliminar \"{list}\"",
"header": "Eliminar esta lista",
"text1": "¿Estás seguro de que quieres eliminar esta lista y todo su contenido?",
"text2": "¡Esto incluye todas las tareas y NO PUEDE SER DESHACIDO!",
"success": "La lista se ha eliminado correctamente."
"title": "Delete \"{list}\"",
"header": "Delete this list",
"text1": "Are you sure you want to delete this list and all of its contents?",
"text2": "This includes all tasks and CANNOT BE UNDONE!",
"success": "The list was successfully deleted."
},
"duplicate": {
"title": "Duplicar esta lista",
"label": "Duplicar",
"title": "Duplicate this list",
"label": "Duplicate",
"text": "Select a namespace which should hold the duplicated list:",
"success": "La lista se ha duplicado exitosamente."
"success": "The list was successfully duplicated."
},
"edit": {
"header": "Editar esta lista",
"title": "Editar \"{list}\"",
"titlePlaceholder": "El título de la lista va aquí…",
"identifierTooltip": "El identificador de lista se puede usar para identificar una tarea de forma única a través de las listas. Puedes establecerlo en blanco para desactivarlo.",
"identifier": "Identificador de la lista",
"identifierPlaceholder": "El identificador de la lista va aquí…",
"description": "Descripción",
"descriptionPlaceholder": "La descripción de la lista va aquí…",
"header": "Edit This List",
"title": "Edit \"{list}\"",
"titlePlaceholder": "The list title goes here…",
"identifierTooltip": "The list identifier can be used to uniquely identify a task across lists. You can set it to empty to disable it.",
"identifier": "List Identifier",
"identifierPlaceholder": "The list identifier goes here…",
"description": "Description",
"descriptionPlaceholder": "The lists description goes here…",
"color": "Color",
"success": "La lista se ha actualizado correctamente."
"success": "The list was successfully updated."
},
"share": {
"header": "Compartir esta lista",
"title": "Compartir \"{list}\"",
"share": "Compartir",
"header": "Share this list",
"title": "Share \"{list}\"",
"share": "Share",
"links": {
"title": "Compartir enlaces",
"what": "¿Qué es un enlace compartido?",
"explanation": "Enlaces compartidos te permiten compartir fácilmente una lista con otros usuarios que no tienen una cuenta de Vikunja.",
"create": "Crear un nuevo enlace compartido",
"name": "Nombre (opcional)",
"title": "Share Links",
"what": "What is a share link?",
"explanation": "Share Links allow you to easily share a list with other users who don't have an account on Vikunja.",
"create": "Create a new link share",
"name": "Name (optional)",
"namePlaceholder": "e.g. Lorem Ipsum",
"nameExplanation": "All actions done by this link share will show up with the name.",
"password": "Contraseña (opcional)",
"password": "Password (optional)",
"passwordExplanation": "When authenticating, the user will be required to enter this password.",
"noName": "Sin nombre establecido",
"remove": "Eliminar un enlace compartido",
"removeText": "¿Está seguro de que desea eliminar este enlace compartido? Ya no será posible acceder a esta lista con este enlace compartido. ¡Esto no se puede deshacer!",
"createSuccess": "El enlace compartido se ha creado correctamente.",
"deleteSuccess": "El enlace compartido se ha eliminado correctamente"
"noName": "No name set",
"remove": "Remove a link share",
"removeText": "Are you sure you want to remove this link share? It will no longer be possible to access this list with this link share. This cannot be undone!",
"createSuccess": "The link share was successfully created.",
"deleteSuccess": "The link share was successfully deleted"
},
"userTeam": {
"typeUser": "usuario | usuarios",
"typeTeam": "equipo | equipos",
"shared": "Compartido con estos {type}",
"you": "",
"notShared": "Aún no se ha compartido con {type}.",
"removeHeader": "Eliminar un {type} de la {sharable}",
"removeText": "¿Estás seguro de que quieres eliminar este {sharable} del {type}? ¡Esto no se puede deshacer!",
"removeSuccess": "El {sharable} fue eliminado correctamente de {type}.",
"addedSuccess": "El {type} se ha añadido correctamente.",
"updatedSuccess": "El {type} fue añadido correctamente."
"typeUser": "user | users",
"typeTeam": "team | teams",
"shared": "Shared with these {type}",
"you": "You",
"notShared": "Not shared with any {type} yet.",
"removeHeader": "Remove a {type} from the {sharable}",
"removeText": "Are you sure you want to remove this {sharable} from the {type}? This cannot be undone!",
"removeSuccess": "The {sharable} was successfully removed from the {type}.",
"addedSuccess": "The {type} was successfully added.",
"updatedSuccess": "The {type} was successfully added."
},
"right": {
"title": "Correcto",
"read": "Solo lectura",
"readWrite": "Lectura y escritura",
"title": "Right",
"read": "Read only",
"readWrite": "Read & write",
"admin": "Admin"
},
"attributes": {
"link": "Enlace",
"name": "Nombre",
"sharedBy": "Compartido por",
"right": "Correcto",
"delete": "Eliminar"
"link": "Link",
"name": "Name",
"sharedBy": "Shared by",
"right": "Right",
"delete": "Delete"
}
},
"list": {
"title": "Lista",
"add": "Añadir",
"addPlaceholder": "Añadir una nueva tarea…",
"empty": "Esta lista está vacía actualmente.",
"newTaskCta": "Crear una nueva tarea.",
"editTask": "Editar Tarea"
"title": "List",
"add": "Add",
"addPlaceholder": "Add a new task…",
"empty": "This list is currently empty.",
"newTaskCta": "Create a new task.",
"editTask": "Edit Task"
},
"gantt": {
"title": "Gantt",
"showTasksWithoutDates": "Mostrar tareas que no tienen fechas establecidas",
"size": "Tamaño",
"default": "Predeterminado",
"month": "Mes",
"day": "Día",
"from": "Desde",
"to": "Hasta",
"noDates": "Esta tarea no tiene fechas establecidas."
"showTasksWithoutDates": "Show tasks which don't have dates set",
"size": "Size",
"default": "Default",
"month": "Month",
"day": "Day",
"from": "From",
"to": "To",
"noDates": "This task has no dates set."
},
"table": {
"title": "Tabla",
"columns": "Columnas"
"title": "Table",
"columns": "Columns"
},
"kanban": {
"title": "Kanban",
"limit": "Límite: {limit}",
"noLimit": "No establecido",
"limit": "Limit: {limit}",
"noLimit": "Not Set",
"doneBucket": "Done bucket",
"doneBucketHint": "All tasks moved into this bucket will automatically marked as done.",
"doneBucketHintExtended": "Todas las tareas que se trasladen al depósito de finalizadas se marcarán como realizadas automáticamente. Todas las tareas marcadas como realizadas desde otro lugar también se moverán.",
"doneBucketHintExtended": "All tasks moved into the done bucket will be marked as done automatically. All tasks marked as done from elsewhere will be moved as well.",
"doneBucketSavedSuccess": "The done bucket has been saved successfully.",
"deleteLast": "You cannot remove the last bucket.",
"addTaskPlaceholder": "Introduce el nuevo título de la tarea…",
"addTask": "Añadir una tarea",
"addAnotherTask": "Añadir otra tarea",
"addTaskPlaceholder": "Enter the new task title…",
"addTask": "Add a task",
"addAnotherTask": "Add another task",
"addBucket": "Create a new bucket",
"addBucketPlaceholder": "Enter the new bucket title…",
"deleteHeaderBucket": "Delete the bucket",
@ -271,82 +272,82 @@
},
"pseudo": {
"favorites": {
"title": "Favoritos"
"title": "Favorites"
}
}
},
"namespace": {
"title": "Namespaces & Lists",
"namespace": "Namespace",
"showArchived": "Mostrar archivados",
"showArchived": "Show Archived",
"noneAvailable": "You don't have any namespaces right now.",
"unarchive": "Des-archivar",
"archived": "Archivado",
"unarchive": "Un-Archive",
"archived": "Archived",
"noLists": "This namespace does not contain any lists.",
"createList": "Create a new list in this namespace.",
"namespaces": "Namespaces",
"search": "Type to search for a namespace…",
"create": {
"title": "Create a new namespace",
"titleRequired": "Por favor, especifica un título.",
"titleRequired": "Please specify a title.",
"explanation": "A namespace is a collection of lists you can share and use to organize your lists with. In fact, every list belongs to a namepace.",
"tooltip": "What's a namespace?",
"success": "The namespace was successfully created."
},
"archive": {
"titleArchive": "Archivar \"{namespace}\"",
"titleUnarchive": "Des-archivar \"{namespace}\"",
"titleArchive": "Archive \"{namespace}\"",
"titleUnarchive": "Un-Archive \"{namespace}\"",
"archiveText": "You won't be able to edit this namespace or create new lists until you un-archive it. This will also archive all lists in this namespace.",
"unarchiveText": "You will be able to create new lists or edit it.",
"success": "The namespace was successfully archived.",
"description": "If a namespace is archived, you cannot create new lists or edit it."
},
"delete": {
"title": "Eliminar \"{namespace}\"",
"title": "Delete \"{namespace}\"",
"text1": "Are you sure you want to delete this namespace and all of its contents?",
"text2": "This includes all lists and tasks and CANNOT BE UNDONE!",
"success": "The namespace was successfully deleted."
},
"edit": {
"title": "Editar \"{namespace}\"",
"title": "Edit \"{namespace}\"",
"success": "The namespace was successfully updated."
},
"share": {
"title": "Compartir \"{namespace}\""
"title": "Share \"{namespace}\""
},
"attributes": {
"title": "Namespace Title",
"titlePlaceholder": "The namespace title goes here…",
"description": "Descripción",
"description": "Description",
"descriptionPlaceholder": "The namespaces description goes here…",
"color": "Color",
"archived": "Está archivado",
"archived": "Is Archived",
"isArchived": "This namespace is archived"
},
"pseudo": {
"sharedLists": {
"title": "Listas compartidas"
"title": "Shared Lists"
},
"favorites": {
"title": "Favoritos"
"title": "Favorites"
},
"savedFilters": {
"title": "Filtros"
"title": "Filters"
}
}
},
"filters": {
"title": "Filtros",
"title": "Filters",
"attributes": {
"title": "Título",
"titlePlaceholder": "El título del filtro guardado va acá…",
"description": "Descripción",
"descriptionPlaceholder": "La descripción va aquí…",
"title": "Title",
"titlePlaceholder": "The saved filter title goes here…",
"description": "Description",
"descriptionPlaceholder": "The description goes here…",
"includeNulls": "Include Tasks which don't have a value set",
"requireAll": "Require all filters to be true for a task to show up",
"showDoneTasks": "Mostrar tareas completadas",
"enablePriority": "Activar filtro por prioridad",
"enablePercentDone": "Activar filtro por porcentaje completado",
"showDoneTasks": "Show Done Tasks",
"enablePriority": "Enable Filter By Priority",
"enablePercentDone": "Enable Filter By Percent Done",
"dueDateRange": "Due Date Range",
"startDateRange": "Start Date Range",
"endDateRange": "End Date Range",
@ -354,11 +355,11 @@
},
"create": {
"title": "Create A Saved Filter",
"description": "Un filtro guardado es una lista virtual que se calcula a partir de un conjunto de filtros cada vez que se accede a él. Una vez creado, aparecerá en un espacio de nombres especial.",
"description": "A saved filter is a virtual list which is computed from a set of filters each time it is accessed. Once created, it will appear in a special namespace.",
"action": "Create new saved filter"
},
"delete": {
"header": "Eliminar este filtro guardado",
"header": "Delete this saved filter",
"text": "Are you sure you want to delete this saved filter?",
"success": "The filter was deleted successfully."
},
@ -374,37 +375,37 @@
"description": "Click on the logo of one of the third-party services below to get started.",
"descriptionDo": "Vikunja will import all lists, tasks, notes, reminders and files you have access to.",
"authorize": "To authorize Vikunja to access your {name} Account, click the button below.",
"getStarted": "Empecemos",
"inProgress": "Importando…",
"getStarted": "Get Started",
"inProgress": "Importing in progress…",
"alreadyMigrated1": "It looks like you've already imported your stuff from {name} at {date}.",
"alreadyMigrated2": "Importing again is possible, but might create duplicates. Are you sure?",
"confirm": "¡Estoy seguro de que empiece a migrar ahora!",
"confirm": "I am sure, please start migrating now!",
"importUpload": "To import data from {name} into Vikunja, click the button below to select a file.",
"upload": "Upload file"
},
"label": {
"title": "Etiquetas",
"manage": "Administrar etiquetas",
"title": "Labels",
"manage": "Manage labels",
"description": "Click on a label to edit it. You can edit all labels you created, you can use all labels which are associated with a task to whose list you have access.",
"newCTA": "You currently do not have any labels.",
"search": "Escribe para buscar una etiqueta…",
"search": "Type to search for a label…",
"create": {
"header": "Nueva etiqueta",
"title": "Crear una nueva etiqueta",
"titleRequired": "Por favor, especifica un título.",
"success": "La etiqueta se ha creado correctamente."
"header": "New label",
"title": "Create a new label",
"titleRequired": "Please specify a title.",
"success": "The label was successfully created."
},
"edit": {
"header": "Editar etiqueta",
"header": "Edit Label",
"forbidden": "You are not allowed to edit this label because you dont own it.",
"success": "La etiqueta se ha actualizado correctamente."
"success": "The label was successfully updated."
},
"deleteSuccess": "La etiqueta se ha eliminado correctamente.",
"deleteSuccess": "The label was successfully deleted.",
"attributes": {
"title": "Título",
"titlePlaceholder": "El título de la etiqueta va aquí…",
"description": "Descripción",
"descriptionPlaceholder": "Descripción de la etiqueta",
"title": "Title",
"titlePlaceholder": "The label title goes here…",
"description": "Description",
"descriptionPlaceholder": "Label description",
"color": "Color"
}
},
@ -417,78 +418,78 @@
"navigation": {
"overview": "Overview",
"upcoming": "Upcoming",
"settings": "Opciones",
"settings": "Settings",
"imprint": "Imprint",
"privacy": "Política de privacidad"
"privacy": "Privacy Policy"
},
"misc": {
"loading": "Cargando…",
"save": "Guardar",
"delete": "Eliminar",
"confirm": "Confirmar",
"cancel": "Cancelar",
"refresh": "Recargar",
"disable": "Desactivar",
"copy": "Copiar al portapapeles",
"search": "Buscar",
"loading": "Loading…",
"save": "Save",
"delete": "Delete",
"confirm": "Confirm",
"cancel": "Cancel",
"refresh": "Refresh",
"disable": "Disable",
"copy": "Copy to clipboard",
"search": "Search",
"searchPlaceholder": "Type to search…",
"previous": "Anterior",
"next": "Siguiente",
"previous": "Previous",
"next": "Next",
"poweredBy": "Powered by Vikunja",
"info": "Información",
"create": "Crear",
"doit": "¡Hazlo!",
"saving": "Guardando…",
"saved": "¡Guardado!",
"default": "Predeterminado",
"close": "Cerrar",
"info": "Info",
"create": "Create",
"doit": "Do it!",
"saving": "Saving…",
"saved": "Saved!",
"default": "Default",
"close": "Close",
"download": "Download"
},
"input": {
"resetColor": "Restablecer color",
"resetColor": "Reset Color",
"datepicker": {
"today": "Hoy",
"tomorrow": "Mañana",
"nextMonday": "El próximo lunes",
"thisWeekend": "Este fin de semana",
"laterThisWeek": "Más tarde esta semana",
"nextWeek": "La próxima semana",
"chooseDate": "Elige una fecha"
"today": "Today",
"tomorrow": "Tomorrow",
"nextMonday": "Next Monday",
"thisWeekend": "This Weekend",
"laterThisWeek": "Later This Week",
"nextWeek": "Next Week",
"chooseDate": "Choose a date"
},
"editor": {
"edit": "Editar",
"done": "Hecho",
"edit": "Edit",
"done": "Done",
"heading1": "Heading 1",
"heading2": "Heading 2",
"heading3": "Heading 3",
"headingSmaller": "Heading Smaller",
"headingBigger": "Heading Bigger",
"bold": "Negrita",
"italic": "Cursiva",
"strikethrough": "Tachado",
"code": "Código",
"quote": "Cita",
"bold": "Bold",
"italic": "Italic",
"strikethrough": "Strikethrough",
"code": "Code",
"quote": "Quote",
"unorderedList": "Unordered List",
"orderedList": "Ordered List",
"cleanBlock": "Clean Block",
"link": "Enlace",
"image": "Imagen",
"table": "Tabla",
"link": "Link",
"image": "Image",
"table": "Table",
"horizontalRule": "Horizontal Rule",
"sideBySide": "Side By Side",
"guide": "Guide"
},
"multiselect": {
"createPlaceholder": "Crear nuevo",
"createPlaceholder": "Create new",
"selectPlaceholder": "Click or press enter to select"
}
},
"task": {
"task": "Tarea",
"new": "Crear una nueva tarea",
"delete": "Eliminar esta tarea",
"task": "Task",
"new": "Create a new task",
"delete": "Delete this task",
"createSuccess": "The task was successfully created.",
"addReminder": "Añadir un nuevo recordatorio…",
"addReminder": "Add a new reminder…",
"doneSuccess": "The task was successfully marked as done.",
"undoneSuccess": "The task was successfully un-marked as done.",
"openDetail": "Open task detail view",
@ -498,35 +499,35 @@
"noDates": "Show tasks without dates",
"current": "Current tasks",
"from": "Tasks from",
"until": "hasta",
"today": "Hoy",
"nextWeek": "La próxima semana",
"nextMonth": "El próximo mes",
"noTasks": "Nada que hacer - ¡Que tengas un buen día!"
"until": "until",
"today": "Today",
"nextWeek": "Next Week",
"nextMonth": "Next Month",
"noTasks": "Nothing to do - Have a nice day!"
},
"detail": {
"chooseDueDate": "Click here to set a due date",
"chooseStartDate": "Click here to set a start date",
"chooseEndDate": "Click here to set an end date",
"move": "Move task to a different list",
"done": "¡Hecho!",
"done": "Done!",
"undone": "Mark as undone",
"created": "Created {0} by {1}",
"updated": "Actualizado {0}",
"doneAt": "Hecho {0}",
"updated": "Updated {0}",
"doneAt": "Done {0}",
"updateSuccess": "The task was saved successfully.",
"deleteSuccess": "The task has been deleted successfully.",
"belongsToList": "This task belongs to list '{list}'",
"due": "Due {at}",
"delete": {
"header": "Eliminar esta tarea",
"header": "Delete this task",
"text1": "Are you sure you want to remove this task?",
"text2": "This will also remove all attachments, reminders and relations associated with this task and cannot be undone!"
},
"actions": {
"assign": "Assign this task to a user",
"label": "Añadir etiquetas",
"priority": "Establecer prioridad",
"label": "Add labels",
"priority": "Set Priority",
"dueDate": "Set Due Date",
"startDate": "Set a Start Date",
"endDate": "Set an End Date",
@ -535,7 +536,7 @@
"percentDone": "Set Percent Done",
"attachments": "Add attachments",
"relatedTasks": "Add task relations",
"moveList": "Mover tarea",
"moveList": "Move task",
"color": "Set task color",
"delete": "Delete task",
"favorite": "Save as favorite",

View File

@ -1 +0,0 @@
{}

View File

@ -113,11 +113,12 @@
"scheduledCancelSuccess": "Мы не будем удалять твой аккаунт."
},
"export": {
"title": "Экспорт данных Vikunja",
"title": "Export your Vikunja data",
"description": "Ты можешь запросить копию всех своих данных Vikunja. Это включает в себя пространства имён, списки, задачи и всё связанное с ними. Эти данные можно будет импортировать на любом экземпляре Vikunja через функцию миграции.",
"descriptionPasswordRequired": "Для продолжения введи свой пароль:",
"request": "Запросить копию моих данных Vikunja",
"success": "Данные Vikunja успешно запрошены! Мы отправим тебе письмо, когда они будут готовы для скачивания."
"success": "Данные Vikunja успешно запрошены! Мы отправим тебе письмо, когда они будут готовы для скачивания.",
"downloadTitle": "Download your exported Vikunja data"
}
},
"list": {

View File

@ -1 +0,0 @@
{}

View File

@ -14,7 +14,10 @@ export const i18n = new VueI18n({
export const availableLanguages = {
en: 'English',
de: 'Deutsch',
'de-DE': 'Deutsch',
'de-swiss': 'Schwizertütsch',
'ru-RU': 'Русский',
'fr-FR': 'Français',
}
const loadedLanguages = ['en'] // our default language that is preloaded
@ -48,18 +51,16 @@ export const loadLanguageAsync = lang => {
export const getCurrentLanguage = () => {
const savedLanguage = localStorage.getItem('language')
if(savedLanguage !== null) {
if (savedLanguage !== null) {
return savedLanguage
}
let browserLanguage = navigator.language || navigator.userLanguage
if (browserLanguage.startsWith('en-')) {
browserLanguage = 'en'
}
if (typeof availableLanguages[browserLanguage] !== 'undefined') {
return browserLanguage
for (let k in availableLanguages) {
if (browserLanguage[k] === browserLanguage || k.startsWith(browserLanguage + '-')) {
return k
}
}
return 'en'

View File

@ -1,5 +0,0 @@
{
"UNKNOWN": 0,
"USER": 1,
"LINK_SHARE": 2
}

View File

@ -2,12 +2,15 @@ import AbstractModel from './abstractModel'
import UserModel from './user'
import {colorIsDark} from '@/helpers/color/colorIsDark'
const DEFAULT_LABEL_BACKGROUND_COLOR = 'e8e8e8'
export default class LabelModel extends AbstractModel {
constructor(data) {
super(data)
// FIXME: this should be empty and be definied in the client.
// that way it get's never send to the server db and is easier to change in future versions.
// Set the default color
if (this.hexColor === '') {
this.hexColor = 'e8e8e8'
this.hexColor = DEFAULT_LABEL_BACKGROUND_COLOR
}
if (this.hexColor.substring(0, 1) !== '#') {
this.hexColor = '#' + this.hexColor

View File

@ -5,7 +5,7 @@ import TaskModel from '@/models/task'
import TaskCommentModel from '@/models/taskComment'
import ListModel from '@/models/list'
import TeamModel from '@/models/team'
import names from './notificationNames.json'
import names from './constants/notificationNames.json'
export default class NotificationModel extends AbstractModel {
constructor(data) {

View File

@ -2,7 +2,7 @@ import AbstractModel from './abstractModel'
import UserModel from './user'
import LabelModel from './label'
import AttachmentModel from './attachment'
import {REPEAT_MODE_DEFAULT} from './taskRepeatModes'
import {REPEAT_MODE_DEFAULT} from './constants/taskRepeatModes'
import SubscriptionModel from '@/models/subscription'
import {parseDateOrNull} from '@/helpers/parseDateOrNull'
@ -15,6 +15,7 @@ export default class TaskModel extends AbstractModel {
super(data)
this.id = Number(this.id)
this.title = this.title?.trim()
this.listId = Number(this.listId)
// Make date objects from timestamps

View File

@ -1,7 +1,7 @@
import {parseTaskText} from './parseTaskText'
import {getDateFromText, getDateFromTextIn} from '../helpers/time/parseDate'
import {calculateDayInterval} from '../helpers/time/calculateDayInterval'
import priorities from '../models/priorities.json'
import priorities from '../models/constants/priorities.json'
describe('Parse Task Text', () => {
it('should return text with no intents as is', () => {

View File

@ -1,5 +1,5 @@
import {parseDate} from '../helpers/time/parseDate'
import _priorities from '../models/priorities.json'
import _priorities from '../models/constants/priorities.json'
const LABEL_PREFIX: string = '@'
const LIST_PREFIX: string = '#'

View File

@ -7,7 +7,9 @@ export default class DataExportService extends AbstractService {
}
download(password) {
const clear = this.setLoading()
return this.getBlobUrl('/user/export/download', 'POST', {password})
.then(url => downloadBlob(url, 'vikunja-export.zip'))
.finally(() => clear())
}
}

View File

@ -39,6 +39,8 @@ export default class TaskService extends AbstractService {
processModel(model) {
model.title = model.title?.trim()
// Ensure that listId is an int
model.listId = Number(model.listId)

View File

@ -59,7 +59,7 @@ export const store = new Vuex.Store({
state.errorMessage = error
},
[ONLINE](state, online) {
state.online = online
state.online = import.meta.env.VITE_IS_ONLINE || online
},
[CURRENT_LIST](state, currentList) {

View File

@ -1,5 +1,7 @@
import Vue from 'vue'
import {findIndexById} from '@/helpers/find'
export default {
namespaced: true,
state: () => ({
@ -15,13 +17,9 @@ export default {
state.attachments.push(attachment)
},
removeById(state, id) {
for (const a in state.attachments) {
if (state.attachments[a].id === id) {
state.attachments.splice(a, 1)
console.debug('Remove attachement', id)
break
}
}
const attachmentIndex = findIndexById(state.attachments, id)
state.attachments.splice(attachmentIndex, 1)
console.debug('Remove attachement', id)
},
},
}

View File

@ -3,6 +3,12 @@ import {ERROR_MESSAGE, LOADING} from '../mutation-types'
import UserModel from '../../models/user'
import {getToken, refreshToken, removeToken, saveToken} from '@/helpers/auth'
const AUTH_TYPES = {
'UNKNOWN': 0,
'USER': 1,
'LINK_SHARE': 2,
}
const defaultSettings = settings => {
if (typeof settings.weekStart === 'undefined' || settings.weekStart === '') {
settings.weekStart = 0
@ -21,6 +27,20 @@ export default {
lastUserInfoRefresh: null,
settings: {},
}),
getters: {
authUser(state) {
return state.authenticated && (
state.info &&
state.info.type === AUTH_TYPES.USER
)
},
authLinkShare(state) {
return state.authenticated && (
state.info &&
state.info.type === AUTH_TYPES.LINK_SHARE
)
},
},
mutations: {
info(state, info) {
state.info = info

View File

@ -1,4 +1,12 @@
@import "../../../node_modules/bulma/bulma";
// utilities are imported in variables.scss
@import "../../../node_modules/bulma/sass/base/_all";
@import "../../../node_modules/bulma/sass/elements/_all";
@import "../../../node_modules/bulma/sass/form/_all";
@import "../../../node_modules/bulma/sass/components/_all";
@import "../../../node_modules/bulma/sass/grid/_all";
@import "../../../node_modules/bulma/sass/helpers/_all";
@import "../../../node_modules/bulma/sass/layout/_all";
@import "fonts";

View File

@ -1,3 +1,5 @@
@import "../../../node_modules/bulma/sass/utilities/_all";
@import 'colors';
@import 'shadows';
@import 'variables';

View File

@ -1,7 +1,7 @@
/* eslint-disable no-console */
/* eslint-disable no-undef */
const workboxVersion = 'v6.2.4'
const workboxVersion = 'v6.3.0'
importScripts( `/workbox-${workboxVersion}/workbox-sw.js`)
workbox.setConfig({modulePathPrefix: `/workbox-${workboxVersion}`})

View File

@ -130,7 +130,7 @@
<div :ref="`tasks-container${bucket.id}`" class="tasks">
<draggable
v-model="bucket.tasks"
@start="() => drag = true"
@start="() => dragstart(bucket)"
@end="updateTaskPosition"
:group="{name: 'tasks', put: shouldAcceptDrop(bucket) && !dragBucket}"
v-bind="dragOptions"
@ -164,7 +164,7 @@
/>
</div>
<p class="help is-danger" v-if="newTaskError[bucket.id] && newTaskText === ''">
{{ $t('list.list.addTitleRequired') }}
{{ $t('list.create.addTitleRequired') }}
</p>
</div>
<x-button
@ -232,14 +232,13 @@
</template>
<script>
import isTouchDevice from 'is-touch-device'
import draggable from 'vuedraggable'
import BucketModel from '../../../models/bucket'
import {filterObject} from '@/helpers/filterObject'
import {mapState} from 'vuex'
import {saveListView} from '@/helpers/saveListView'
import Rights from '../../../models/rights.json'
import Rights from '../../../models/constants/rights.json'
import {LOADING, LOADING_MODULE} from '@/store/mutation-types'
import FilterPopup from '@/components/list/partials/filter-popup.vue'
import Dropdown from '@/components/misc/dropdown.vue'
@ -315,10 +314,8 @@ export default {
animation: 150,
ghostClass: 'ghost',
dragClass: 'task-dragging',
}
if (isTouchDevice()) {
options.delay = 150
delay: 150,
delayOnTouchOnly: true,
}
return options
@ -542,6 +539,10 @@ export default {
bucket.limit === 0 || // If there is no limit set, dragging & dropping should always work
bucket.tasks.length < bucket.limit // Disallow dropping to buckets which have their limit reached
},
dragstart(bucket) {
this.drag = true
this.sourceBucket = bucket.id
},
toggleDoneBucket(bucket) {
bucket.isDoneBucket = !bucket.isDoneBucket
this.$store.dispatch('kanban/updateBucket', bucket)

View File

@ -177,7 +177,7 @@ import AddTask from '../../../components/tasks/add-task'
import SingleTaskInList from '../../../components/tasks/partials/singleTaskInList'
import taskList from '../../../components/tasks/mixins/taskList'
import {saveListView} from '@/helpers/saveListView'
import Rights from '../../../models/rights.json'
import Rights from '../../../models/constants/rights.json'
import FilterPopup from '@/components/list/partials/filter-popup.vue'
import {HAS_TASKS} from '@/store/mutation-types'
import Nothing from '@/components/misc/nothing.vue'

View File

@ -286,10 +286,26 @@ export default {
},
methods: {
initTasks(page, search = '') {
// This makes sure an id sort order is always sorted last.
// When tasks would be sorted first by id and then by whatever else was specified, the id sort takes
// precedence over everything else, making any other sort columns pretty useless.
const sortKeys = Object.keys(this.sortBy)
let hasIdFilter = false
for (const s of sortKeys) {
if (s === 'id') {
sortKeys.splice(s, 1)
hasIdFilter = true
break
}
}
if (hasIdFilter) {
sortKeys.push('id')
}
const params = this.params
params.sort_by = []
params.order_by = []
Object.keys(this.sortBy).map(s => {
sortKeys.map(s => {
params.sort_by.push(s)
params.order_by.push(this.sortBy[s])
})

View File

@ -33,8 +33,7 @@
</template>
<script>
import {mapState} from 'vuex'
import authTypes from '@/models/authTypes.json'
import {mapGetters} from 'vuex'
export default {
name: 'LinkSharingAuth',
@ -54,9 +53,9 @@ export default {
mounted() {
this.setTitle(this.$t('sharing.authenticating'))
},
computed: mapState({
authLinkShare: state => state.auth.authenticated && (state.auth.info && state.auth.info.type === authTypes.LINK_SHARE),
}),
computed: mapGetters('auth', [
'authLinkShare',
]),
methods: {
auth() {
this.errorMessage = ''

View File

@ -419,10 +419,10 @@
<script>
import TaskService from '../../services/task'
import TaskModel from '../../models/task'
import relationKinds from '../../models/relationKinds.json'
import relationKinds from '../../models/constants/relationKinds.json'
import priorites from '../../models/priorities.json'
import rights from '../../models/rights.json'
import priorites from '../../models/constants/priorities.json'
import rights from '../../models/constants/rights.json'
import PrioritySelect from '../../components/tasks/partials/prioritySelect'
import PercentDoneSelect from '../../components/tasks/partials/percentDoneSelect'

View File

@ -167,7 +167,7 @@ import TeamMemberService from '../../services/teamMember'
import TeamMemberModel from '../../models/teamMember'
import UserModel from '../../models/user'
import UserService from '../../services/user'
import Rights from '../../models/rights.json'
import Rights from '../../models/constants/rights.json'
import LoadingComponent from '../../components/misc/loading'
import ErrorComponent from '../../components/misc/error'

View File

@ -71,8 +71,8 @@
{{ $t('user.settings.general.language') }}
</span>
<div class="select ml-2">
<select v-model.number="language">
<option :value="l" v-for="(lang, l) in availableLanguages" :key="l">{{ lang }}</option>
<select v-model="language">
<option :value="lang.code" v-for="lang in availableLanguages" :key="lang.code">{{ lang.title }}</option>
</select>
</div>
</label>
@ -288,7 +288,7 @@ import TotpService from '../../services/totp'
import UserSettingsService from '../../services/userSettings'
import UserSettingsModel from '../../models/userSettings'
import {playSoundWhenDoneKey} from '@/helpers/playPop'
import {availableLanguages, saveLanguage, getCurrentLanguage} from '@/i18n/setup'
import {availableLanguages, saveLanguage, getCurrentLanguage} from '../../i18n/setup'
import {mapState} from 'vuex'
@ -367,7 +367,9 @@ export default {
return `${apiBase}/dav/principals/${this.userInfo.username}/`
},
availableLanguages() {
return availableLanguages
return Object.entries(availableLanguages)
.map(l => ({code: l[0], title: l[1]}))
.sort((a, b) => a.title > b.title)
},
...mapState({
totpEnabled: state => state.config.totpEnabled,

1790
yarn.lock

File diff suppressed because it is too large Load Diff