forked from vikunja/frontend
Compare commits
1 Commits
main
...
feature/cr
Author | SHA1 | Date | |
---|---|---|---|
acf52fbe16 |
51
.drone.yml
51
.drone.yml
|
@ -341,7 +341,7 @@ trigger:
|
|||
- "refs/tags/**"
|
||||
|
||||
steps:
|
||||
- name: docker-unstable
|
||||
- name: docker-latest
|
||||
image: plugins/docker:linux-arm
|
||||
pull: true
|
||||
settings:
|
||||
|
@ -350,7 +350,7 @@ steps:
|
|||
password:
|
||||
from_secret: docker_password
|
||||
repo: vikunja/frontend
|
||||
tags: unstable-linux-arm
|
||||
tags: latest-linux-arm
|
||||
build_args:
|
||||
- USE_RELEASE=true
|
||||
- RELEASE_VERSION=unstable
|
||||
|
@ -380,7 +380,7 @@ steps:
|
|||
depends_on:
|
||||
- clone
|
||||
|
||||
- name: docker-unstable-arm64
|
||||
- name: docker-latest-arm64
|
||||
image: plugins/docker:linux-arm64
|
||||
pull: true
|
||||
settings:
|
||||
|
@ -389,7 +389,7 @@ steps:
|
|||
password:
|
||||
from_secret: docker_password
|
||||
repo: vikunja/frontend
|
||||
tags: unstable-linux-arm64
|
||||
tags: latest-linux-arm64
|
||||
build_args:
|
||||
- USE_RELEASE=true
|
||||
- RELEASE_VERSION=unstable
|
||||
|
@ -438,7 +438,7 @@ trigger:
|
|||
- "refs/tags/**"
|
||||
|
||||
steps:
|
||||
- name: docker-unstable
|
||||
- name: docker-latest
|
||||
image: plugins/docker:linux-amd64
|
||||
pull: true
|
||||
settings:
|
||||
|
@ -447,7 +447,7 @@ steps:
|
|||
password:
|
||||
from_secret: docker_password
|
||||
repo: vikunja/frontend
|
||||
tags: unstable-linux-amd64
|
||||
tags: latest-linux-amd64
|
||||
build_args:
|
||||
- USE_RELEASE=true
|
||||
- RELEASE_VERSION=unstable
|
||||
|
@ -488,12 +488,12 @@ depends_on:
|
|||
- docker-arm-release
|
||||
|
||||
steps:
|
||||
- name: manifest-unstable
|
||||
- name: manifest-latest
|
||||
pull: always
|
||||
image: plugins/manifest
|
||||
settings:
|
||||
tags: unstable
|
||||
spec: docker-manifest-unstable.tmpl
|
||||
tags: latest
|
||||
spec: docker-manifest-latest.tmpl
|
||||
password:
|
||||
from_secret: docker_password
|
||||
username:
|
||||
|
@ -516,23 +516,6 @@ steps:
|
|||
when:
|
||||
ref:
|
||||
- "refs/tags/**"
|
||||
|
||||
- name: manifest-release-latest
|
||||
pull: always
|
||||
image: plugins/manifest
|
||||
depends_on:
|
||||
- clone
|
||||
settings:
|
||||
tags: latest
|
||||
ignore_missing: true
|
||||
spec: docker-manifest.tmpl
|
||||
password:
|
||||
from_secret: docker_password
|
||||
username:
|
||||
from_secret: docker_username
|
||||
when:
|
||||
ref:
|
||||
- "refs/tags/**"
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
|
@ -572,8 +555,8 @@ kind: pipeline
|
|||
type: docker
|
||||
name: update-translations
|
||||
|
||||
depends_on:
|
||||
- build
|
||||
#depends_on:
|
||||
# - build
|
||||
|
||||
trigger:
|
||||
branch:
|
||||
|
@ -594,19 +577,9 @@ 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]
|
||||
|
@ -620,8 +593,6 @@ steps:
|
|||
- name: upload
|
||||
pull: always
|
||||
image: jonasfranz/crowdin
|
||||
depends_on:
|
||||
- clone
|
||||
settings:
|
||||
files:
|
||||
en.json: src/i18n/lang/en.json
|
||||
|
|
346
CHANGELOG.md
346
CHANGELOG.md
|
@ -2,347 +2,13 @@
|
|||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres
|
||||
to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
All releases can be found on https://code.vikunja.io/frontend/releases.
|
||||
|
||||
The releases aim at the api versions which is why there are missing versions.
|
||||
|
||||
## [0.18.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
|
||||
|
@ -482,8 +148,7 @@ The releases aim at the api versions which is why there are missing versions.
|
|||
* Make sure all arm64 build steps run in parallel
|
||||
* Make sure all empty pages have a call to action
|
||||
* Make sure all popups & dropdowns are animated
|
||||
* Make sure attachements are only added once to the list after uploading + Make sure the attachment list shows up every
|
||||
time after adding an attachment
|
||||
* Make sure attachements are only added once to the list after uploading + Make sure the attachment list shows up every time after adding an attachment
|
||||
* Make sure no cta's are visible while the page is loading
|
||||
* Make sure the loading spinner is always visible at the end of the page
|
||||
* Make the button shadow lighter
|
||||
|
@ -1010,7 +675,7 @@ The releases aim at the api versions which is why there are missing versions.
|
|||
* Hide totp settings if it is disabled server side
|
||||
* Increase network timeout when building docker image
|
||||
* Make sure the version includes the tag when building docker images
|
||||
* # PrideMonth
|
||||
* #PrideMonth
|
||||
* Only renew user token on tab focus events
|
||||
* Redirect the user to login page if the token expired when the tab gets focus again
|
||||
* Remove title length restrictions
|
||||
|
@ -1045,7 +710,7 @@ The releases aim at the api versions which is why there are missing versions.
|
|||
|
||||
## [0.13] - 2020-05-12
|
||||
|
||||
#### Added
|
||||
#### Added
|
||||
|
||||
* Add docker run script to change api url on startup
|
||||
* Add github token for renovate (#89)
|
||||
|
@ -1390,7 +1055,6 @@ The releases aim at the api versions which is why there are missing versions.
|
|||
* Use email instead of username when resetting a password
|
||||
|
||||
### Fixed
|
||||
|
||||
* Fixed trying to verify an email when there was none
|
||||
* Fixed loading tasks when the user was not authenticated
|
||||
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
|
||||
[![Build Status](https://drone.kolaente.de/api/badges/vikunja/frontend/status.svg)](https://drone.kolaente.de/vikunja/frontend)
|
||||
[![License: AGPL v3](https://img.shields.io/badge/License-AGPL%20v3-blue.svg)](LICENSE)
|
||||
[![Download](https://img.shields.io/badge/download-v0.18.1-brightgreen.svg)](https://dl.vikunja.io)
|
||||
[![Translation](https://badges.crowdin.net/vikunja/localized.svg)](https://crowdin.com/project/vikunja)
|
||||
[![Download](https://img.shields.io/badge/download-v0.17.0-brightgreen.svg)](https://dl.vikunja.io)
|
||||
[![Translation](https://hosted.weblate.org/widgets/vikunja/-/frontend/svg-badge.svg)](https://hosted.weblate.org/engage/vikunja/)
|
||||
|
||||
This is the web frontend for Vikunja, written in Vue.js.
|
||||
|
||||
|
|
24
crowdin.yml
Normal file
24
crowdin.yml
Normal file
|
@ -0,0 +1,24 @@
|
|||
project_id: 462614
|
||||
api_token_env: CROWDIN_API_TOKEN
|
||||
base_path: .
|
||||
base_url: https://api.crowdin.com
|
||||
|
||||
preserve_hierarchy: false
|
||||
|
||||
files:
|
||||
- source: /src/i18n/lang/*.json
|
||||
#
|
||||
# Where translations will be placed
|
||||
# e.g. "/resources/%two_letters_code%/%original_file_name%"
|
||||
#
|
||||
translation: /src/i18n/lang/%two_letters_code%/%original_file_name%
|
||||
#
|
||||
# files or directories for ignore
|
||||
# e.g. ["/**/?.txt", "/**/[0-9].txt", "/**/*\?*.txt"]
|
||||
#
|
||||
#"ignore" : [],
|
||||
#
|
||||
# The dest allows you to specify a file name in Crowdin
|
||||
# e.g. "/messages.json"
|
||||
#
|
||||
#"dest" : "",
|
|
@ -1,17 +1,17 @@
|
|||
image: vikunja/frontend:unstable
|
||||
image: vikunja/frontend:latest
|
||||
manifests:
|
||||
-
|
||||
image: vikunja/frontend:unstable-linux-amd64
|
||||
image: vikunja/frontend:latest-linux-amd64
|
||||
platform:
|
||||
architecture: amd64
|
||||
os: linux
|
||||
-
|
||||
image: vikunja/frontend:unstable-linux-arm64
|
||||
image: vikunja/frontend:latest-linux-arm64
|
||||
platform:
|
||||
architecture: arm64
|
||||
os: linux
|
||||
-
|
||||
image: vikunja/frontend:unstable-linux-arm
|
||||
image: vikunja/frontend:latest-linux-arm
|
||||
platform:
|
||||
architecture: arm
|
||||
os: linux
|
26
package.json
26
package.json
|
@ -14,7 +14,7 @@
|
|||
"test:frontend": "cypress run"
|
||||
},
|
||||
"dependencies": {
|
||||
"browserslist": "4.17.0",
|
||||
"browserslist": "4.16.8",
|
||||
"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.3",
|
||||
"marked": "3.0.2",
|
||||
"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.3.0"
|
||||
"workbox-precaching": "6.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@4tw/cypress-drag-drop": "2.0.0",
|
||||
"@4tw/cypress-drag-drop": "1.8.1",
|
||||
"@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.31.0",
|
||||
"@typescript-eslint/parser": "4.31.0",
|
||||
"@typescript-eslint/eslint-plugin": "4.30.0",
|
||||
"@typescript-eslint/parser": "4.30.0",
|
||||
"@vue/babel-preset-app": "4.5.13",
|
||||
"@vue/eslint-config-typescript": "7.0.0",
|
||||
"autoprefixer": "10.3.4",
|
||||
"axios": "0.21.4",
|
||||
"axios": "0.21.3",
|
||||
"babel-eslint": "10.1.0",
|
||||
"cypress": "8.3.1",
|
||||
"cypress-file-upload": "5.0.8",
|
||||
"esbuild": "0.12.26",
|
||||
"esbuild": "0.12.25",
|
||||
"eslint": "7.32.0",
|
||||
"eslint-plugin-vue": "7.17.0",
|
||||
"express": "4.17.1",
|
||||
"faker": "5.5.3",
|
||||
"jest": "27.1.1",
|
||||
"jest": "27.1.0",
|
||||
"rollup-plugin-terser": "7.0.2",
|
||||
"rollup-plugin-visualizer": "5.5.2",
|
||||
"sass": "1.39.2",
|
||||
"sass": "1.39.0",
|
||||
"ts-jest": "27.0.5",
|
||||
"typescript": "4.4.2",
|
||||
"vite": "2.5.6",
|
||||
"vite": "2.5.3",
|
||||
"vite-plugin-pwa": "0.11.2",
|
||||
"vite-plugin-vue2": "1.8.2",
|
||||
"vite-plugin-vue2": "1.8.1",
|
||||
"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.3.0"
|
||||
"workbox-cli": "6.2.4"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
|
|
10
src/App.vue
10
src/App.vue
|
@ -23,9 +23,11 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import {mapState, mapGetters} from 'vuex'
|
||||
import {mapState} 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'
|
||||
|
@ -72,13 +74,11 @@ 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() {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
</a>
|
||||
<div
|
||||
:class="{'has-background': background}"
|
||||
:style="{'background-image': background && `url(${background})`}"
|
||||
:style="{'background-image': `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',
|
||||
background: 'background', // FIXME: Return the full thing or nothing at all to prevent calls to /null
|
||||
menuActive: MENU_ACTIVE,
|
||||
userInfo: state => state.auth.info,
|
||||
authenticated: state => state.auth.authenticated,
|
||||
|
|
|
@ -97,7 +97,7 @@
|
|||
<script>
|
||||
import {mapState} from 'vuex'
|
||||
import {CURRENT_LIST, QUICK_ACTIONS_ACTIVE} from '@/store/mutation-types'
|
||||
import Rights from '@/models/constants/rights.json'
|
||||
import Rights from '@/models/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'
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
:checked="checked"
|
||||
:disabled="disabled"
|
||||
:id="checkBoxId"
|
||||
@change="(event) => updateData(event.target.checked)"
|
||||
@change="updateData"
|
||||
style="display: none;"
|
||||
type="checkbox"/>
|
||||
<label :for="checkBoxId" class="check">
|
||||
|
@ -51,10 +51,10 @@ export default {
|
|||
this.checkBoxId = 'fancycheckbox' + Math.random()
|
||||
},
|
||||
methods: {
|
||||
updateData(checked) {
|
||||
this.checked = checked
|
||||
this.$emit('input', checked)
|
||||
this.$emit('change', checked)
|
||||
updateData(e) {
|
||||
this.checked = e.target.checked
|
||||
this.$emit('input', this.checked)
|
||||
this.$emit('change', e.target.checked)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -190,35 +190,6 @@ 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: {
|
||||
|
@ -231,8 +202,32 @@ export default {
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
params: DEFAULT_PARAMS,
|
||||
filters: DEFAULT_FILTERS,
|
||||
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: '',
|
||||
},
|
||||
|
||||
usersService: UserService,
|
||||
foundusers: [],
|
||||
|
|
|
@ -36,7 +36,14 @@
|
|||
<div class="migration-in-progress">
|
||||
<img :alt="name" :src="`/images/migration/${identifier}.png`"/>
|
||||
<div class="progress-dots">
|
||||
<span v-for="i in progressDotsCount" :key="i" />
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
</div>
|
||||
<img alt="Vikunja" src="/images/logo.svg">
|
||||
</div>
|
||||
|
@ -67,19 +74,15 @@
|
|||
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: {
|
||||
|
|
|
@ -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,24 +46,23 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
const API_DEFAULT_PORT = 3456
|
||||
|
||||
export default {
|
||||
name: 'apiConfig',
|
||||
data() {
|
||||
return {
|
||||
configureApi: false,
|
||||
apiUrl: window.API_URL,
|
||||
apiUrl: '',
|
||||
errorMsg: '',
|
||||
successMsg: '',
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.apiUrl = window.API_URL
|
||||
if (this.apiUrl === '') {
|
||||
this.configureApi = true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
methods: {
|
||||
apiDomain() {
|
||||
if (window.API_URL.startsWith('/api/v1')) {
|
||||
return window.location.host
|
||||
|
@ -73,8 +72,6 @@ export default {
|
|||
.split(/[/?#]/)
|
||||
return urlParts[0]
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
setApiUrl() {
|
||||
if (this.apiUrl === '') {
|
||||
return
|
||||
|
@ -134,17 +131,17 @@ export default {
|
|||
return Promise.reject(e)
|
||||
})
|
||||
.catch((e) => {
|
||||
// Check if it is reachable at port API_DEFAULT_PORT and https
|
||||
if (urlToCheck.port !== API_DEFAULT_PORT) {
|
||||
// Check if it is reachable at port 3456 and https
|
||||
if (urlToCheck.port !== 3456) {
|
||||
urlToCheck.protocol = 'https:'
|
||||
urlToCheck.port = API_DEFAULT_PORT
|
||||
urlToCheck.port = 3456
|
||||
window.API_URL = urlToCheck.toString()
|
||||
return this.$store.dispatch('config/update')
|
||||
}
|
||||
return Promise.reject(e)
|
||||
})
|
||||
.catch((e) => {
|
||||
// Check if it is reachable at :API_DEFAULT_PORT and /api/v1 and https
|
||||
// Check if it is reachable at :3456 and /api/v1 and https
|
||||
urlToCheck.pathname = origUrlToCheck.pathname
|
||||
if (
|
||||
!urlToCheck.pathname.endsWith('/api/v1') &&
|
||||
|
@ -157,17 +154,17 @@ export default {
|
|||
return Promise.reject(e)
|
||||
})
|
||||
.catch((e) => {
|
||||
// Check if it is reachable at port API_DEFAULT_PORT and http
|
||||
if (urlToCheck.port !== API_DEFAULT_PORT) {
|
||||
// Check if it is reachable at port 3456 and http
|
||||
if (urlToCheck.port !== 3456) {
|
||||
urlToCheck.protocol = 'http:'
|
||||
urlToCheck.port = API_DEFAULT_PORT
|
||||
urlToCheck.port = 3456
|
||||
window.API_URL = urlToCheck.toString()
|
||||
return this.$store.dispatch('config/update')
|
||||
}
|
||||
return Promise.reject(e)
|
||||
})
|
||||
.catch((e) => {
|
||||
// Check if it is reachable at :API_DEFAULT_PORT and /api/v1 and http
|
||||
// Check if it is reachable at :3456 and /api/v1 and http
|
||||
urlToCheck.pathname = origUrlToCheck.pathname
|
||||
if (
|
||||
!urlToCheck.pathname.endsWith('/api/v1') &&
|
||||
|
@ -182,14 +179,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
|
||||
|
|
|
@ -50,7 +50,7 @@
|
|||
<script>
|
||||
import NotificationService from '@/services/notification'
|
||||
import User from '@/components/misc/user.vue'
|
||||
import names from '@/models/constants/notificationNames.json'
|
||||
import names from '@/models/notificationNames.json'
|
||||
import {closeWhenClickedOutside} from '@/helpers/closeWhenClickedOutside'
|
||||
import {mapState} from 'vuex'
|
||||
|
||||
|
|
|
@ -173,7 +173,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import rights from '../../models/constants/rights'
|
||||
import rights from '../../models/rights'
|
||||
|
||||
import LinkShareService from '../../services/linkShare'
|
||||
import LinkShareModel from '../../models/linkShare'
|
||||
|
|
|
@ -145,7 +145,7 @@ import TeamListService from '../../services/teamList'
|
|||
import TeamService from '../../services/team'
|
||||
import TeamModel from '../../models/team'
|
||||
|
||||
import rights from '../../models/constants/rights.json'
|
||||
import rights from '../../models/rights'
|
||||
import Multiselect from '@/components/input/multiselect.vue'
|
||||
import Nothing from '@/components/misc/nothing.vue'
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@
|
|||
import ListService from '../../services/list'
|
||||
import TaskService from '../../services/task'
|
||||
import TaskModel from '../../models/task'
|
||||
import priorities from '../../models/constants/priorities'
|
||||
import priorities from '../../models/priorities'
|
||||
import EditLabels from './partials/editLabels'
|
||||
import Reminders from './partials/reminders'
|
||||
import ColorPicker from '../input/colorPicker'
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
</x-button>
|
||||
</div>
|
||||
<filter-popup
|
||||
@change="loadTasks()"
|
||||
@change="loadTasks"
|
||||
:visible="showTaskFilter"
|
||||
v-model="params"
|
||||
/>
|
||||
|
@ -24,7 +24,10 @@
|
|||
class="month"
|
||||
v-for="(m, mk) in days[yk]"
|
||||
>
|
||||
{{ formatYear(new Date(`${yk}-${parseInt(mk) + 1}-01`)) }}
|
||||
{{
|
||||
new Date(new Date(`${yk}-${parseInt(mk) + 1}-01`)).toLocaleString('en-us', {month: 'long'})
|
||||
}},
|
||||
{{ new Date(yk).getFullYear() }}
|
||||
<div class="days">
|
||||
<div
|
||||
:class="{ today: d.toDateString() === now.toDateString() }"
|
||||
|
@ -188,13 +191,12 @@ import EditTask from './edit-task'
|
|||
|
||||
import TaskService from '../../services/task'
|
||||
import TaskModel from '../../models/task'
|
||||
import priorities from '../../models/constants/priorities'
|
||||
import priorities from '../../models/priorities'
|
||||
import PriorityLabel from './partials/priorityLabel'
|
||||
import TaskCollectionService from '../../services/taskCollection'
|
||||
import {mapState} from 'vuex'
|
||||
import Rights from '../../models/constants/rights.json'
|
||||
import Rights from '../../models/rights.json'
|
||||
import FilterPopup from '@/components/list/partials/filter-popup.vue'
|
||||
import {format} from 'date-fns'
|
||||
|
||||
export default {
|
||||
name: 'GanttChart',
|
||||
|
@ -479,9 +481,6 @@ export default {
|
|||
this.error(e)
|
||||
})
|
||||
},
|
||||
formatYear(date) {
|
||||
return format(date, 'MMMM, yyyy')
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,70 +1,17 @@
|
|||
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: new TaskCollectionService(),
|
||||
taskCollectionService: TaskCollectionService,
|
||||
tasks: [],
|
||||
|
||||
pages: [],
|
||||
currentPage: 0,
|
||||
|
||||
loadedList: null,
|
||||
|
@ -73,21 +20,27 @@ export default {
|
|||
searchTerm: '',
|
||||
|
||||
showTaskFilter: false,
|
||||
params: DEFAULT_PARAMS,
|
||||
params: {
|
||||
sort_by: ['position', 'id'],
|
||||
order_by: ['asc', 'desc'],
|
||||
filter_by: ['done'],
|
||||
filter_value: ['false'],
|
||||
filter_comparator: ['equals'],
|
||||
filter_concat: 'and',
|
||||
},
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
// Only listen for query path changes
|
||||
'$route.query': {
|
||||
handler: 'loadTasksForPage',
|
||||
immediate: true,
|
||||
},
|
||||
'$route.query': 'loadTasksForPage', // Only listen for query path changes
|
||||
'$route.path': 'loadTasksOnSavedFilter',
|
||||
},
|
||||
computed: {
|
||||
pages() {
|
||||
return createPagination(this.taskCollectionService.totalPages, this.currentPage)
|
||||
},
|
||||
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()
|
||||
},
|
||||
methods: {
|
||||
loadTasks(
|
||||
|
@ -127,20 +80,48 @@ export default {
|
|||
return
|
||||
}
|
||||
|
||||
this.tasks = []
|
||||
this.$set(this, 'tasks', [])
|
||||
|
||||
this.taskCollectionService.getAll(list, params, page)
|
||||
.then(r => {
|
||||
this.tasks = r
|
||||
this.$set(this, 'tasks', r)
|
||||
this.$set(this, 'pages', [])
|
||||
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)
|
||||
|
@ -196,6 +177,17 @@ 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
|
||||
|
||||
|
@ -213,6 +205,5 @@ export default {
|
|||
this.error(e)
|
||||
})
|
||||
},
|
||||
getRouteForPagination,
|
||||
},
|
||||
}
|
|
@ -7,17 +7,16 @@
|
|||
<h1
|
||||
class="title input"
|
||||
:class="{'disabled': !canWrite}"
|
||||
@blur="save($event.target.textContent)"
|
||||
@keydown.enter.prevent.stop="$event.target.blur()"
|
||||
@focusout="save()"
|
||||
@keydown.enter.prevent.stop="save()"
|
||||
: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 && showSavedMessage">
|
||||
<span class="has-text-success is-inline-flex is-align-content-center" v-if="!loading && saved">
|
||||
<icon icon="check" class="mr-2"/>
|
||||
{{ $t('misc.saved') }}
|
||||
</span>
|
||||
|
@ -26,22 +25,22 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import {LOADING} from '@/store/mutation-types'
|
||||
import {mapState} from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'heading',
|
||||
data() {
|
||||
return {
|
||||
showSavedMessage: false,
|
||||
task: {title: '', identifier: '', index:''},
|
||||
taskTitle: '',
|
||||
saved: 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']),
|
||||
task() {
|
||||
return this.value
|
||||
},
|
||||
},
|
||||
computed: mapState({
|
||||
loading: LOADING,
|
||||
}),
|
||||
props: {
|
||||
value: {
|
||||
required: true,
|
||||
|
@ -51,29 +50,43 @@ 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(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 (title === this.task.title) {
|
||||
return
|
||||
}
|
||||
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()
|
||||
// if the task title changed.
|
||||
if (this.task.title !== this.taskTitle) {
|
||||
this.$refs.taskTitle.blur()
|
||||
this.saveTask()
|
||||
this.taskTitle = taskTitle
|
||||
}
|
||||
},
|
||||
saveTask() {
|
||||
this.saving = true
|
||||
|
||||
const newTask = {
|
||||
...this.task,
|
||||
title,
|
||||
}
|
||||
|
||||
this.$store.dispatch('tasks/update', newTask)
|
||||
.then((task) => {
|
||||
this.$emit('input', task)
|
||||
this.showSavedMessage = true
|
||||
this.$store.dispatch('tasks/update', this.task)
|
||||
.then(() => {
|
||||
this.$emit('input', this.task)
|
||||
this.saved = true
|
||||
setTimeout(() => {
|
||||
this.showSavedMessage = false
|
||||
this.saved = false
|
||||
}, 2000)
|
||||
})
|
||||
.catch(e => {
|
||||
|
|
|
@ -92,10 +92,8 @@ export default {
|
|||
methods: {
|
||||
markTaskAsDone(task) {
|
||||
this.loadingInternal = true
|
||||
this.$store.dispatch('tasks/update', {
|
||||
...task,
|
||||
done: !task.done,
|
||||
})
|
||||
task.done = !task.done
|
||||
this.$store.dispatch('tasks/update', task)
|
||||
.then(() => {
|
||||
if (task.done) {
|
||||
playPop()
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import priorites from '../../../models/constants/priorities'
|
||||
import priorites from '../../../models/priorities'
|
||||
|
||||
export default {
|
||||
name: 'priorityLabel',
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import priorites from '../../../models/constants/priorities'
|
||||
import priorites from '../../../models/priorities'
|
||||
|
||||
export default {
|
||||
name: 'prioritySelect',
|
||||
|
|
|
@ -122,7 +122,7 @@
|
|||
import TaskService from '../../../services/task'
|
||||
import TaskModel from '../../../models/task'
|
||||
import TaskRelationService from '../../../services/taskRelation'
|
||||
import relationKinds from '../../../models/constants/relationKinds'
|
||||
import relationKinds from '../../../models/relationKinds'
|
||||
import TaskRelationModel from '../../../models/taskRelation'
|
||||
|
||||
import Multiselect from '@/components/input/multiselect.vue'
|
||||
|
|
|
@ -51,7 +51,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import repeatModes from '@/models/constants/taskRepeatModes'
|
||||
import repeatModes from '@/models/taskRepeatModes'
|
||||
|
||||
export default {
|
||||
name: 'repeatAfter',
|
||||
|
@ -62,7 +62,7 @@ export default {
|
|||
amount: 0,
|
||||
type: '',
|
||||
},
|
||||
repeatModes,
|
||||
repeatModes: repeatModes,
|
||||
}
|
||||
},
|
||||
props: {
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
export function findIndexById(array : [], id : string | number) {
|
||||
return array.findIndex(({id: currentId}) => currentId === id)
|
||||
}
|
|
@ -113,12 +113,11 @@
|
|||
"scheduledCancelSuccess": "Wir werden deinen Account nicht löschen."
|
||||
},
|
||||
"export": {
|
||||
"title": "Export your Vikunja data",
|
||||
"title": "Exportiere die Daten deines Vikunja-Accounts",
|
||||
"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.",
|
||||
"downloadTitle": "Download your exported Vikunja data"
|
||||
"success": "Du hast deine Daten bei Vikunja erfolgreich angefordert! Wir schicken dir eine E-Mail, sobald sie zum Download bereitstehen."
|
||||
}
|
||||
},
|
||||
"list": {
|
309
src/i18n/lang/de.json
Normal file
309
src/i18n/lang/de.json
Normal file
|
@ -0,0 +1,309 @@
|
|||
{
|
||||
"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}"
|
||||
}
|
||||
}
|
|
@ -113,12 +113,11 @@
|
|||
"scheduledCancelSuccess": "Wir werden deinen Account nicht löschen."
|
||||
},
|
||||
"export": {
|
||||
"title": "Export your Vikunja data",
|
||||
"title": "Exportiere die Daten deines Vikunja-Accounts",
|
||||
"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.",
|
||||
"downloadTitle": "Download your exported Vikunja data"
|
||||
"success": "Du hast deine Daten bei Vikunja erfolgreich angefordert! Wir schicken dir eine E-Mail, sobald sie zum Download bereitstehen."
|
||||
}
|
||||
},
|
||||
"list": {
|
|
@ -113,12 +113,11 @@
|
|||
"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.",
|
||||
"downloadTitle": "Download your exported Vikunja data"
|
||||
"success": "You've successfully requested your Vikunja Data! We will send you an email once it's ready to download."
|
||||
}
|
||||
},
|
||||
"list": {
|
||||
|
|
|
@ -1,101 +1,101 @@
|
|||
{
|
||||
"home": {
|
||||
"welcomeNight": "Good Night {username}",
|
||||
"welcomeMorning": "Good Morning {username}",
|
||||
"welcomeDay": "Hi {username}",
|
||||
"welcomeEvening": "Good Evening {username}",
|
||||
"lastViewed": "Last viewed",
|
||||
"welcomeNight": "Buenas noches {username}",
|
||||
"welcomeMorning": "Buenos días {username}",
|
||||
"welcomeDay": "Hola {username}",
|
||||
"welcomeEvening": "Buenas tardes {username}",
|
||||
"lastViewed": "Visto por última vez",
|
||||
"list": {
|
||||
"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"
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"title": "Not found",
|
||||
"text": "The page you requested does not exist."
|
||||
"title": "No encontrado",
|
||||
"text": "La página solicitada no existe."
|
||||
},
|
||||
"user": {
|
||||
"auth": {
|
||||
"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!",
|
||||
"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!",
|
||||
"openIdGeneralError": "An error occured while authenticating against the third party.",
|
||||
"logout": "Logout"
|
||||
"logout": "Cerrar sesión"
|
||||
},
|
||||
"settings": {
|
||||
"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.",
|
||||
"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.",
|
||||
"general": {
|
||||
"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"
|
||||
"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"
|
||||
},
|
||||
"totp": {
|
||||
"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."
|
||||
"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."
|
||||
},
|
||||
"caldav": {
|
||||
"title": "Caldav",
|
||||
"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"
|
||||
"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"
|
||||
},
|
||||
"avatar": {
|
||||
"title": "Avatar",
|
||||
"initials": "Initials",
|
||||
"initials": "Iniciales",
|
||||
"gravatar": "Gravatar",
|
||||
"upload": "Upload",
|
||||
"uploadAvatar": "Upload Avatar",
|
||||
"statusUpdateSuccess": "Avatar status was updated successfully!",
|
||||
"setSuccess": "The avatar has been set successfully!"
|
||||
"upload": "Subir",
|
||||
"uploadAvatar": "Subir Avatar",
|
||||
"statusUpdateSuccess": "¡El estado del avatar se ha actualizado correctamente!",
|
||||
"setSuccess": "¡El avatar se ha establecido correctamente!"
|
||||
}
|
||||
},
|
||||
"deletion": {
|
||||
|
@ -113,153 +113,152 @@
|
|||
"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.",
|
||||
"downloadTitle": "Download your exported Vikunja data"
|
||||
"success": "You've successfully requested your Vikunja Data! We will send you an email once it's ready to download."
|
||||
}
|
||||
},
|
||||
"list": {
|
||||
"archived": "This list is archived. It is not possible to create new or edit tasks or it.",
|
||||
"title": "List Title",
|
||||
"archived": "Esta lista está archivada. No es posible crear nuevas o editar tareas o ello.",
|
||||
"title": "Título de Lista",
|
||||
"color": "Color",
|
||||
"lists": "Lists",
|
||||
"search": "Type to search for a list…",
|
||||
"searchSelect": "Click or press enter to select this list",
|
||||
"shared": "Shared Lists",
|
||||
"lists": "Listas",
|
||||
"search": "Escribe para buscar una lista…",
|
||||
"searchSelect": "Haga clic o presione enter para seleccionar esta lista",
|
||||
"shared": "Listas compartidas",
|
||||
"create": {
|
||||
"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."
|
||||
"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."
|
||||
},
|
||||
"archive": {
|
||||
"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."
|
||||
"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."
|
||||
},
|
||||
"background": {
|
||||
"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!"
|
||||
"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!"
|
||||
},
|
||||
"delete": {
|
||||
"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."
|
||||
"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."
|
||||
},
|
||||
"duplicate": {
|
||||
"title": "Duplicate this list",
|
||||
"label": "Duplicate",
|
||||
"title": "Duplicar esta lista",
|
||||
"label": "Duplicar",
|
||||
"text": "Select a namespace which should hold the duplicated list:",
|
||||
"success": "The list was successfully duplicated."
|
||||
"success": "La lista se ha duplicado exitosamente."
|
||||
},
|
||||
"edit": {
|
||||
"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…",
|
||||
"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í…",
|
||||
"color": "Color",
|
||||
"success": "The list was successfully updated."
|
||||
"success": "La lista se ha actualizado correctamente."
|
||||
},
|
||||
"share": {
|
||||
"header": "Share this list",
|
||||
"title": "Share \"{list}\"",
|
||||
"share": "Share",
|
||||
"header": "Compartir esta lista",
|
||||
"title": "Compartir \"{list}\"",
|
||||
"share": "Compartir",
|
||||
"links": {
|
||||
"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)",
|
||||
"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)",
|
||||
"namePlaceholder": "e.g. Lorem Ipsum",
|
||||
"nameExplanation": "All actions done by this link share will show up with the name.",
|
||||
"password": "Password (optional)",
|
||||
"password": "Contraseña (opcional)",
|
||||
"passwordExplanation": "When authenticating, the user will be required to enter this password.",
|
||||
"noName": "No name set",
|
||||
"remove": "Remove a link share",
|
||||
"removeText": "Are you sure you want to remove this link share? It will no longer be possible to access this list with this link share. This cannot be undone!",
|
||||
"createSuccess": "The link share was successfully created.",
|
||||
"deleteSuccess": "The link share was successfully deleted"
|
||||
"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"
|
||||
},
|
||||
"userTeam": {
|
||||
"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."
|
||||
"typeUser": "usuario | usuarios",
|
||||
"typeTeam": "equipo | equipos",
|
||||
"shared": "Compartido con estos {type}",
|
||||
"you": "Tú",
|
||||
"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."
|
||||
},
|
||||
"right": {
|
||||
"title": "Right",
|
||||
"read": "Read only",
|
||||
"readWrite": "Read & write",
|
||||
"title": "Correcto",
|
||||
"read": "Solo lectura",
|
||||
"readWrite": "Lectura y escritura",
|
||||
"admin": "Admin"
|
||||
},
|
||||
"attributes": {
|
||||
"link": "Link",
|
||||
"name": "Name",
|
||||
"sharedBy": "Shared by",
|
||||
"right": "Right",
|
||||
"delete": "Delete"
|
||||
"link": "Enlace",
|
||||
"name": "Nombre",
|
||||
"sharedBy": "Compartido por",
|
||||
"right": "Correcto",
|
||||
"delete": "Eliminar"
|
||||
}
|
||||
},
|
||||
"list": {
|
||||
"title": "List",
|
||||
"add": "Add",
|
||||
"addPlaceholder": "Add a new task…",
|
||||
"empty": "This list is currently empty.",
|
||||
"newTaskCta": "Create a new task.",
|
||||
"editTask": "Edit Task"
|
||||
"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"
|
||||
},
|
||||
"gantt": {
|
||||
"title": "Gantt",
|
||||
"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."
|
||||
"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."
|
||||
},
|
||||
"table": {
|
||||
"title": "Table",
|
||||
"columns": "Columns"
|
||||
"title": "Tabla",
|
||||
"columns": "Columnas"
|
||||
},
|
||||
"kanban": {
|
||||
"title": "Kanban",
|
||||
"limit": "Limit: {limit}",
|
||||
"noLimit": "Not Set",
|
||||
"limit": "Límite: {limit}",
|
||||
"noLimit": "No establecido",
|
||||
"doneBucket": "Done bucket",
|
||||
"doneBucketHint": "All tasks moved into this bucket will automatically marked as done.",
|
||||
"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.",
|
||||
"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.",
|
||||
"doneBucketSavedSuccess": "The done bucket has been saved successfully.",
|
||||
"deleteLast": "You cannot remove the last bucket.",
|
||||
"addTaskPlaceholder": "Enter the new task title…",
|
||||
"addTask": "Add a task",
|
||||
"addAnotherTask": "Add another task",
|
||||
"addTaskPlaceholder": "Introduce el nuevo título de la tarea…",
|
||||
"addTask": "Añadir una tarea",
|
||||
"addAnotherTask": "Añadir otra tarea",
|
||||
"addBucket": "Create a new bucket",
|
||||
"addBucketPlaceholder": "Enter the new bucket title…",
|
||||
"deleteHeaderBucket": "Delete the bucket",
|
||||
|
@ -272,82 +271,82 @@
|
|||
},
|
||||
"pseudo": {
|
||||
"favorites": {
|
||||
"title": "Favorites"
|
||||
"title": "Favoritos"
|
||||
}
|
||||
}
|
||||
},
|
||||
"namespace": {
|
||||
"title": "Namespaces & Lists",
|
||||
"namespace": "Namespace",
|
||||
"showArchived": "Show Archived",
|
||||
"showArchived": "Mostrar archivados",
|
||||
"noneAvailable": "You don't have any namespaces right now.",
|
||||
"unarchive": "Un-Archive",
|
||||
"archived": "Archived",
|
||||
"unarchive": "Des-archivar",
|
||||
"archived": "Archivado",
|
||||
"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": "Please specify a title.",
|
||||
"titleRequired": "Por favor, especifica un título.",
|
||||
"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": "Archive \"{namespace}\"",
|
||||
"titleUnarchive": "Un-Archive \"{namespace}\"",
|
||||
"titleArchive": "Archivar \"{namespace}\"",
|
||||
"titleUnarchive": "Des-archivar \"{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": "Delete \"{namespace}\"",
|
||||
"title": "Eliminar \"{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": "Edit \"{namespace}\"",
|
||||
"title": "Editar \"{namespace}\"",
|
||||
"success": "The namespace was successfully updated."
|
||||
},
|
||||
"share": {
|
||||
"title": "Share \"{namespace}\""
|
||||
"title": "Compartir \"{namespace}\""
|
||||
},
|
||||
"attributes": {
|
||||
"title": "Namespace Title",
|
||||
"titlePlaceholder": "The namespace title goes here…",
|
||||
"description": "Description",
|
||||
"description": "Descripción",
|
||||
"descriptionPlaceholder": "The namespaces description goes here…",
|
||||
"color": "Color",
|
||||
"archived": "Is Archived",
|
||||
"archived": "Está archivado",
|
||||
"isArchived": "This namespace is archived"
|
||||
},
|
||||
"pseudo": {
|
||||
"sharedLists": {
|
||||
"title": "Shared Lists"
|
||||
"title": "Listas compartidas"
|
||||
},
|
||||
"favorites": {
|
||||
"title": "Favorites"
|
||||
"title": "Favoritos"
|
||||
},
|
||||
"savedFilters": {
|
||||
"title": "Filters"
|
||||
"title": "Filtros"
|
||||
}
|
||||
}
|
||||
},
|
||||
"filters": {
|
||||
"title": "Filters",
|
||||
"title": "Filtros",
|
||||
"attributes": {
|
||||
"title": "Title",
|
||||
"titlePlaceholder": "The saved filter title goes here…",
|
||||
"description": "Description",
|
||||
"descriptionPlaceholder": "The description goes here…",
|
||||
"title": "Título",
|
||||
"titlePlaceholder": "El título del filtro guardado va acá…",
|
||||
"description": "Descripción",
|
||||
"descriptionPlaceholder": "La descripción va aquí…",
|
||||
"includeNulls": "Include Tasks which don't have a value set",
|
||||
"requireAll": "Require all filters to be true for a task to show up",
|
||||
"showDoneTasks": "Show Done Tasks",
|
||||
"enablePriority": "Enable Filter By Priority",
|
||||
"enablePercentDone": "Enable Filter By Percent Done",
|
||||
"showDoneTasks": "Mostrar tareas completadas",
|
||||
"enablePriority": "Activar filtro por prioridad",
|
||||
"enablePercentDone": "Activar filtro por porcentaje completado",
|
||||
"dueDateRange": "Due Date Range",
|
||||
"startDateRange": "Start Date Range",
|
||||
"endDateRange": "End Date Range",
|
||||
|
@ -355,11 +354,11 @@
|
|||
},
|
||||
"create": {
|
||||
"title": "Create A Saved Filter",
|
||||
"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.",
|
||||
"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.",
|
||||
"action": "Create new saved filter"
|
||||
},
|
||||
"delete": {
|
||||
"header": "Delete this saved filter",
|
||||
"header": "Eliminar este filtro guardado",
|
||||
"text": "Are you sure you want to delete this saved filter?",
|
||||
"success": "The filter was deleted successfully."
|
||||
},
|
||||
|
@ -375,37 +374,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": "Get Started",
|
||||
"inProgress": "Importing in progress…",
|
||||
"getStarted": "Empecemos",
|
||||
"inProgress": "Importando…",
|
||||
"alreadyMigrated1": "It looks like you've already imported your stuff from {name} at {date}.",
|
||||
"alreadyMigrated2": "Importing again is possible, but might create duplicates. Are you sure?",
|
||||
"confirm": "I am sure, please start migrating now!",
|
||||
"confirm": "¡Estoy seguro de que empiece a migrar ahora!",
|
||||
"importUpload": "To import data from {name} into Vikunja, click the button below to select a file.",
|
||||
"upload": "Upload file"
|
||||
},
|
||||
"label": {
|
||||
"title": "Labels",
|
||||
"manage": "Manage labels",
|
||||
"title": "Etiquetas",
|
||||
"manage": "Administrar etiquetas",
|
||||
"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": "Type to search for a label…",
|
||||
"search": "Escribe para buscar una etiqueta…",
|
||||
"create": {
|
||||
"header": "New label",
|
||||
"title": "Create a new label",
|
||||
"titleRequired": "Please specify a title.",
|
||||
"success": "The label was successfully created."
|
||||
"header": "Nueva etiqueta",
|
||||
"title": "Crear una nueva etiqueta",
|
||||
"titleRequired": "Por favor, especifica un título.",
|
||||
"success": "La etiqueta se ha creado correctamente."
|
||||
},
|
||||
"edit": {
|
||||
"header": "Edit Label",
|
||||
"header": "Editar etiqueta",
|
||||
"forbidden": "You are not allowed to edit this label because you dont own it.",
|
||||
"success": "The label was successfully updated."
|
||||
"success": "La etiqueta se ha actualizado correctamente."
|
||||
},
|
||||
"deleteSuccess": "The label was successfully deleted.",
|
||||
"deleteSuccess": "La etiqueta se ha eliminado correctamente.",
|
||||
"attributes": {
|
||||
"title": "Title",
|
||||
"titlePlaceholder": "The label title goes here…",
|
||||
"description": "Description",
|
||||
"descriptionPlaceholder": "Label description",
|
||||
"title": "Título",
|
||||
"titlePlaceholder": "El título de la etiqueta va aquí…",
|
||||
"description": "Descripción",
|
||||
"descriptionPlaceholder": "Descripción de la etiqueta",
|
||||
"color": "Color"
|
||||
}
|
||||
},
|
||||
|
@ -418,78 +417,78 @@
|
|||
"navigation": {
|
||||
"overview": "Overview",
|
||||
"upcoming": "Upcoming",
|
||||
"settings": "Settings",
|
||||
"settings": "Opciones",
|
||||
"imprint": "Imprint",
|
||||
"privacy": "Privacy Policy"
|
||||
"privacy": "Política de privacidad"
|
||||
},
|
||||
"misc": {
|
||||
"loading": "Loading…",
|
||||
"save": "Save",
|
||||
"delete": "Delete",
|
||||
"confirm": "Confirm",
|
||||
"cancel": "Cancel",
|
||||
"refresh": "Refresh",
|
||||
"disable": "Disable",
|
||||
"copy": "Copy to clipboard",
|
||||
"search": "Search",
|
||||
"loading": "Cargando…",
|
||||
"save": "Guardar",
|
||||
"delete": "Eliminar",
|
||||
"confirm": "Confirmar",
|
||||
"cancel": "Cancelar",
|
||||
"refresh": "Recargar",
|
||||
"disable": "Desactivar",
|
||||
"copy": "Copiar al portapapeles",
|
||||
"search": "Buscar",
|
||||
"searchPlaceholder": "Type to search…",
|
||||
"previous": "Previous",
|
||||
"next": "Next",
|
||||
"previous": "Anterior",
|
||||
"next": "Siguiente",
|
||||
"poweredBy": "Powered by Vikunja",
|
||||
"info": "Info",
|
||||
"create": "Create",
|
||||
"doit": "Do it!",
|
||||
"saving": "Saving…",
|
||||
"saved": "Saved!",
|
||||
"default": "Default",
|
||||
"close": "Close",
|
||||
"info": "Información",
|
||||
"create": "Crear",
|
||||
"doit": "¡Hazlo!",
|
||||
"saving": "Guardando…",
|
||||
"saved": "¡Guardado!",
|
||||
"default": "Predeterminado",
|
||||
"close": "Cerrar",
|
||||
"download": "Download"
|
||||
},
|
||||
"input": {
|
||||
"resetColor": "Reset Color",
|
||||
"resetColor": "Restablecer color",
|
||||
"datepicker": {
|
||||
"today": "Today",
|
||||
"tomorrow": "Tomorrow",
|
||||
"nextMonday": "Next Monday",
|
||||
"thisWeekend": "This Weekend",
|
||||
"laterThisWeek": "Later This Week",
|
||||
"nextWeek": "Next Week",
|
||||
"chooseDate": "Choose a date"
|
||||
"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"
|
||||
},
|
||||
"editor": {
|
||||
"edit": "Edit",
|
||||
"done": "Done",
|
||||
"edit": "Editar",
|
||||
"done": "Hecho",
|
||||
"heading1": "Heading 1",
|
||||
"heading2": "Heading 2",
|
||||
"heading3": "Heading 3",
|
||||
"headingSmaller": "Heading Smaller",
|
||||
"headingBigger": "Heading Bigger",
|
||||
"bold": "Bold",
|
||||
"italic": "Italic",
|
||||
"strikethrough": "Strikethrough",
|
||||
"code": "Code",
|
||||
"quote": "Quote",
|
||||
"bold": "Negrita",
|
||||
"italic": "Cursiva",
|
||||
"strikethrough": "Tachado",
|
||||
"code": "Código",
|
||||
"quote": "Cita",
|
||||
"unorderedList": "Unordered List",
|
||||
"orderedList": "Ordered List",
|
||||
"cleanBlock": "Clean Block",
|
||||
"link": "Link",
|
||||
"image": "Image",
|
||||
"table": "Table",
|
||||
"link": "Enlace",
|
||||
"image": "Imagen",
|
||||
"table": "Tabla",
|
||||
"horizontalRule": "Horizontal Rule",
|
||||
"sideBySide": "Side By Side",
|
||||
"guide": "Guide"
|
||||
},
|
||||
"multiselect": {
|
||||
"createPlaceholder": "Create new",
|
||||
"createPlaceholder": "Crear nuevo",
|
||||
"selectPlaceholder": "Click or press enter to select"
|
||||
}
|
||||
},
|
||||
"task": {
|
||||
"task": "Task",
|
||||
"new": "Create a new task",
|
||||
"delete": "Delete this task",
|
||||
"task": "Tarea",
|
||||
"new": "Crear una nueva tarea",
|
||||
"delete": "Eliminar esta tarea",
|
||||
"createSuccess": "The task was successfully created.",
|
||||
"addReminder": "Add a new reminder…",
|
||||
"addReminder": "Añadir un nuevo recordatorio…",
|
||||
"doneSuccess": "The task was successfully marked as done.",
|
||||
"undoneSuccess": "The task was successfully un-marked as done.",
|
||||
"openDetail": "Open task detail view",
|
||||
|
@ -499,35 +498,35 @@
|
|||
"noDates": "Show tasks without dates",
|
||||
"current": "Current tasks",
|
||||
"from": "Tasks from",
|
||||
"until": "until",
|
||||
"today": "Today",
|
||||
"nextWeek": "Next Week",
|
||||
"nextMonth": "Next Month",
|
||||
"noTasks": "Nothing to do - Have a nice day!"
|
||||
"until": "hasta",
|
||||
"today": "Hoy",
|
||||
"nextWeek": "La próxima semana",
|
||||
"nextMonth": "El próximo mes",
|
||||
"noTasks": "Nada que hacer - ¡Que tengas un buen día!"
|
||||
},
|
||||
"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": "Done!",
|
||||
"done": "¡Hecho!",
|
||||
"undone": "Mark as undone",
|
||||
"created": "Created {0} by {1}",
|
||||
"updated": "Updated {0}",
|
||||
"doneAt": "Done {0}",
|
||||
"updated": "Actualizado {0}",
|
||||
"doneAt": "Hecho {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": "Delete this task",
|
||||
"header": "Eliminar esta tarea",
|
||||
"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": "Add labels",
|
||||
"priority": "Set Priority",
|
||||
"label": "Añadir etiquetas",
|
||||
"priority": "Establecer prioridad",
|
||||
"dueDate": "Set Due Date",
|
||||
"startDate": "Set a Start Date",
|
||||
"endDate": "Set an End Date",
|
||||
|
@ -536,7 +535,7 @@
|
|||
"percentDone": "Set Percent Done",
|
||||
"attachments": "Add attachments",
|
||||
"relatedTasks": "Add task relations",
|
||||
"moveList": "Move task",
|
||||
"moveList": "Mover tarea",
|
||||
"color": "Set task color",
|
||||
"delete": "Delete task",
|
||||
"favorite": "Save as favorite",
|
1
src/i18n/lang/es.json
Normal file
1
src/i18n/lang/es.json
Normal file
|
@ -0,0 +1 @@
|
|||
{}
|
1
src/i18n/lang/fr.json
Normal file
1
src/i18n/lang/fr.json
Normal file
|
@ -0,0 +1 @@
|
|||
{}
|
|
@ -113,12 +113,11 @@
|
|||
"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.",
|
||||
"downloadTitle": "Download your exported Vikunja data"
|
||||
"success": "You've successfully requested your Vikunja Data! We will send you an email once it's ready to download."
|
||||
}
|
||||
},
|
||||
"list": {
|
|
@ -113,12 +113,11 @@
|
|||
"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.",
|
||||
"downloadTitle": "Download your exported Vikunja data"
|
||||
"success": "You've successfully requested your Vikunja Data! We will send you an email once it's ready to download."
|
||||
}
|
||||
},
|
||||
"list": {
|
|
@ -1,34 +1,34 @@
|
|||
{
|
||||
"home": {
|
||||
"welcomeNight": "Good Night {username}",
|
||||
"welcomeMorning": "Good Morning {username}",
|
||||
"welcomeDay": "Hi {username}",
|
||||
"welcomeEvening": "Good Evening {username}",
|
||||
"lastViewed": "Last viewed",
|
||||
"welcomeNight": "Boa noite {username}",
|
||||
"welcomeMorning": "Bom dia {username}",
|
||||
"welcomeDay": "Olá {username}",
|
||||
"welcomeEvening": "Boa tarde {username}",
|
||||
"lastViewed": "Recentes",
|
||||
"list": {
|
||||
"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"
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"title": "Not found",
|
||||
"text": "The page you requested does not exist."
|
||||
"title": "Não encontrado",
|
||||
"text": "A página solicitada não existe."
|
||||
},
|
||||
"user": {
|
||||
"auth": {
|
||||
"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.",
|
||||
"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.",
|
||||
"passwordsDontMatch": "Passwords don't match",
|
||||
"confirmEmailSuccess": "You successfully confirmed your email! You can log in now.",
|
||||
"totpTitle": "Two Factor Authentication Code",
|
||||
|
@ -113,12 +113,11 @@
|
|||
"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.",
|
||||
"downloadTitle": "Download your exported Vikunja data"
|
||||
"success": "You've successfully requested your Vikunja Data! We will send you an email once it's ready to download."
|
||||
}
|
||||
},
|
||||
"list": {
|
1
src/i18n/lang/ro.json
Normal file
1
src/i18n/lang/ro.json
Normal file
|
@ -0,0 +1 @@
|
|||
{}
|
|
@ -113,12 +113,11 @@
|
|||
"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.",
|
||||
"downloadTitle": "Download your exported Vikunja data"
|
||||
"success": "You've successfully requested your Vikunja Data! We will send you an email once it's ready to download."
|
||||
}
|
||||
},
|
||||
"list": {
|
1
src/i18n/lang/ru.json
Normal file
1
src/i18n/lang/ru.json
Normal file
|
@ -0,0 +1 @@
|
|||
{}
|
|
@ -113,12 +113,11 @@
|
|||
"scheduledCancelSuccess": "Мы не будем удалять твой аккаунт."
|
||||
},
|
||||
"export": {
|
||||
"title": "Export your Vikunja data",
|
||||
"title": "Экспорт данных Vikunja",
|
||||
"description": "Ты можешь запросить копию всех своих данных Vikunja. Это включает в себя пространства имён, списки, задачи и всё связанное с ними. Эти данные можно будет импортировать на любом экземпляре Vikunja через функцию миграции.",
|
||||
"descriptionPasswordRequired": "Для продолжения введи свой пароль:",
|
||||
"request": "Запросить копию моих данных Vikunja",
|
||||
"success": "Данные Vikunja успешно запрошены! Мы отправим тебе письмо, когда они будут готовы для скачивания.",
|
||||
"downloadTitle": "Download your exported Vikunja data"
|
||||
"success": "Данные Vikunja успешно запрошены! Мы отправим тебе письмо, когда они будут готовы для скачивания."
|
||||
}
|
||||
},
|
||||
"list": {
|
|
@ -14,10 +14,7 @@ export const i18n = new VueI18n({
|
|||
|
||||
export const availableLanguages = {
|
||||
en: 'English',
|
||||
'de-DE': 'Deutsch',
|
||||
'de-swiss': 'Schwizertütsch',
|
||||
'ru-RU': 'Русский',
|
||||
'fr-FR': 'Français',
|
||||
de: 'Deutsch',
|
||||
}
|
||||
|
||||
const loadedLanguages = ['en'] // our default language that is preloaded
|
||||
|
@ -51,16 +48,18 @@ 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
|
||||
|
||||
for (let k in availableLanguages) {
|
||||
if (browserLanguage[k] === browserLanguage || k.startsWith(browserLanguage + '-')) {
|
||||
return k
|
||||
}
|
||||
if (browserLanguage.startsWith('en-')) {
|
||||
browserLanguage = 'en'
|
||||
}
|
||||
|
||||
if (typeof availableLanguages[browserLanguage] !== 'undefined') {
|
||||
return browserLanguage
|
||||
}
|
||||
|
||||
return 'en'
|
||||
|
|
5
src/models/authTypes.json
Normal file
5
src/models/authTypes.json
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"UNKNOWN": 0,
|
||||
"USER": 1,
|
||||
"LINK_SHARE": 2
|
||||
}
|
|
@ -2,15 +2,12 @@ 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 = DEFAULT_LABEL_BACKGROUND_COLOR
|
||||
this.hexColor = 'e8e8e8'
|
||||
}
|
||||
if (this.hexColor.substring(0, 1) !== '#') {
|
||||
this.hexColor = '#' + this.hexColor
|
||||
|
|
|
@ -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 './constants/notificationNames.json'
|
||||
import names from './notificationNames.json'
|
||||
|
||||
export default class NotificationModel extends AbstractModel {
|
||||
constructor(data) {
|
||||
|
|
|
@ -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 './constants/taskRepeatModes'
|
||||
import {REPEAT_MODE_DEFAULT} from './taskRepeatModes'
|
||||
|
||||
import SubscriptionModel from '@/models/subscription'
|
||||
import {parseDateOrNull} from '@/helpers/parseDateOrNull'
|
||||
|
@ -15,7 +15,6 @@ 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
|
||||
|
|
|
@ -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/constants/priorities.json'
|
||||
import priorities from '../models/priorities.json'
|
||||
|
||||
describe('Parse Task Text', () => {
|
||||
it('should return text with no intents as is', () => {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import {parseDate} from '../helpers/time/parseDate'
|
||||
import _priorities from '../models/constants/priorities.json'
|
||||
import _priorities from '../models/priorities.json'
|
||||
|
||||
const LABEL_PREFIX: string = '@'
|
||||
const LIST_PREFIX: string = '#'
|
||||
|
|
|
@ -7,9 +7,7 @@ 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())
|
||||
}
|
||||
}
|
|
@ -39,8 +39,6 @@ export default class TaskService extends AbstractService {
|
|||
|
||||
processModel(model) {
|
||||
|
||||
model.title = model.title?.trim()
|
||||
|
||||
// Ensure that listId is an int
|
||||
model.listId = Number(model.listId)
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ export const store = new Vuex.Store({
|
|||
state.errorMessage = error
|
||||
},
|
||||
[ONLINE](state, online) {
|
||||
state.online = import.meta.env.VITE_IS_ONLINE || online
|
||||
state.online = online
|
||||
},
|
||||
[CURRENT_LIST](state, currentList) {
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
import Vue from 'vue'
|
||||
|
||||
import {findIndexById} from '@/helpers/find'
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state: () => ({
|
||||
|
@ -17,9 +15,13 @@ export default {
|
|||
state.attachments.push(attachment)
|
||||
},
|
||||
removeById(state, id) {
|
||||
const attachmentIndex = findIndexById(state.attachments, id)
|
||||
state.attachments.splice(attachmentIndex, 1)
|
||||
console.debug('Remove attachement', id)
|
||||
for (const a in state.attachments) {
|
||||
if (state.attachments[a].id === id) {
|
||||
state.attachments.splice(a, 1)
|
||||
console.debug('Remove attachement', id)
|
||||
break
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
|
@ -3,12 +3,6 @@ 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
|
||||
|
@ -27,20 +21,6 @@ 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
|
||||
|
|
|
@ -1,12 +1,4 @@
|
|||
// 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 "../../../node_modules/bulma/bulma";
|
||||
|
||||
@import "fonts";
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
@import "../../../node_modules/bulma/sass/utilities/_all";
|
||||
|
||||
@import 'colors';
|
||||
@import 'shadows';
|
||||
@import 'variables';
|
|
@ -1,7 +1,7 @@
|
|||
/* eslint-disable no-console */
|
||||
/* eslint-disable no-undef */
|
||||
|
||||
const workboxVersion = 'v6.3.0'
|
||||
const workboxVersion = 'v6.2.4'
|
||||
importScripts( `/workbox-${workboxVersion}/workbox-sw.js`)
|
||||
workbox.setConfig({modulePathPrefix: `/workbox-${workboxVersion}`})
|
||||
|
||||
|
|
|
@ -130,7 +130,7 @@
|
|||
<div :ref="`tasks-container${bucket.id}`" class="tasks">
|
||||
<draggable
|
||||
v-model="bucket.tasks"
|
||||
@start="() => dragstart(bucket)"
|
||||
@start="() => drag = true"
|
||||
@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.create.addTitleRequired') }}
|
||||
{{ $t('list.list.addTitleRequired') }}
|
||||
</p>
|
||||
</div>
|
||||
<x-button
|
||||
|
@ -239,7 +239,7 @@ import BucketModel from '../../../models/bucket'
|
|||
import {filterObject} from '@/helpers/filterObject'
|
||||
import {mapState} from 'vuex'
|
||||
import {saveListView} from '@/helpers/saveListView'
|
||||
import Rights from '../../../models/constants/rights.json'
|
||||
import Rights from '../../../models/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'
|
||||
|
@ -542,10 +542,6 @@ 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)
|
||||
|
|
|
@ -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/constants/rights.json'
|
||||
import Rights from '../../../models/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'
|
||||
|
|
|
@ -286,26 +286,10 @@ 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 = []
|
||||
sortKeys.map(s => {
|
||||
Object.keys(this.sortBy).map(s => {
|
||||
params.sort_by.push(s)
|
||||
params.order_by.push(this.sortBy[s])
|
||||
})
|
||||
|
|
|
@ -33,7 +33,8 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import {mapGetters} from 'vuex'
|
||||
import {mapState} from 'vuex'
|
||||
import authTypes from '@/models/authTypes.json'
|
||||
|
||||
export default {
|
||||
name: 'LinkSharingAuth',
|
||||
|
@ -53,9 +54,9 @@ export default {
|
|||
mounted() {
|
||||
this.setTitle(this.$t('sharing.authenticating'))
|
||||
},
|
||||
computed: mapGetters('auth', [
|
||||
'authLinkShare',
|
||||
]),
|
||||
computed: mapState({
|
||||
authLinkShare: state => state.auth.authenticated && (state.auth.info && state.auth.info.type === authTypes.LINK_SHARE),
|
||||
}),
|
||||
methods: {
|
||||
auth() {
|
||||
this.errorMessage = ''
|
||||
|
|
|
@ -419,10 +419,10 @@
|
|||
<script>
|
||||
import TaskService from '../../services/task'
|
||||
import TaskModel from '../../models/task'
|
||||
import relationKinds from '../../models/constants/relationKinds.json'
|
||||
import relationKinds from '../../models/relationKinds.json'
|
||||
|
||||
import priorites from '../../models/constants/priorities.json'
|
||||
import rights from '../../models/constants/rights.json'
|
||||
import priorites from '../../models/priorities.json'
|
||||
import rights from '../../models/rights.json'
|
||||
|
||||
import PrioritySelect from '../../components/tasks/partials/prioritySelect'
|
||||
import PercentDoneSelect from '../../components/tasks/partials/percentDoneSelect'
|
||||
|
|
|
@ -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/constants/rights.json'
|
||||
import Rights from '../../models/rights.json'
|
||||
|
||||
import LoadingComponent from '../../components/misc/loading'
|
||||
import ErrorComponent from '../../components/misc/error'
|
||||
|
|
|
@ -71,8 +71,8 @@
|
|||
{{ $t('user.settings.general.language') }}
|
||||
</span>
|
||||
<div class="select ml-2">
|
||||
<select v-model="language">
|
||||
<option :value="lang.code" v-for="lang in availableLanguages" :key="lang.code">{{ lang.title }}</option>
|
||||
<select v-model.number="language">
|
||||
<option :value="l" v-for="(lang, l) in availableLanguages" :key="l">{{ lang }}</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,9 +367,7 @@ export default {
|
|||
return `${apiBase}/dav/principals/${this.userInfo.username}/`
|
||||
},
|
||||
availableLanguages() {
|
||||
return Object.entries(availableLanguages)
|
||||
.map(l => ({code: l[0], title: l[1]}))
|
||||
.sort((a, b) => a.title > b.title)
|
||||
return availableLanguages
|
||||
},
|
||||
...mapState({
|
||||
totpEnabled: state => state.config.totpEnabled,
|
||||
|
|
Loading…
Reference in New Issue
Block a user