forked from vikunja/frontend
Compare commits
56 Commits
9aaac69e29
...
1efe096968
Author | SHA1 | Date |
---|---|---|
Adrian Simmons | 1efe096968 | |
Adrian Simmons | 8e4095e1bd | |
renovate | e535584412 | |
renovate | c5b9e2a1ff | |
Dominik Pschenitschni | e45bc83132 | |
Dominik Pschenitschni | bc8b04fc7a | |
Dominik Pschenitschni | 84284a6211 | |
Dominik Pschenitschni | 716de2c99c | |
Dominik Pschenitschni | 769d94e879 | |
Dominik Pschenitschni | baa86530c8 | |
renovate | 7613afbf27 | |
renovate | aeb886e4c5 | |
renovate | 306bd1c179 | |
renovate | f3cf79fa65 | |
renovate | 8c945b049a | |
renovate | f69111c105 | |
renovate | 03afbfc6c8 | |
renovate | c07288dd8b | |
renovate | 03f3c52548 | |
renovate | 384037c694 | |
kolaente | 734db0795c | |
renovate | 8f6c0f3738 | |
drone | f3324c6aaf | |
konrad | f8d009a6aa | |
dpschen | 59e915cc10 | |
dpschen | 0c9dad9891 | |
dpschen | b7ad29f056 | |
renovate | a7434f24df | |
drone | f61d5bac46 | |
renovate | 2911dee3cc | |
kolaente | 58c361ee29 | |
renovate | d3fc1439b5 | |
renovate | bb544c353c | |
renovate | cc90a1cea5 | |
renovate | cffba33748 | |
renovate | 055e0a2901 | |
renovate | c8d1921bcd | |
renovate | 9e09314f75 | |
renovate | c3833d90d8 | |
renovate | e24cb55e1b | |
renovate | f897611ad1 | |
renovate | ea8fe297d9 | |
renovate | c6b604f1fa | |
renovate | 4792adfbd1 | |
dpschen | 04c94418d7 | |
renovate | 12bec04c42 | |
renovate | e8eb94d71b | |
renovate | 5137f9f6cb | |
renovate | a2f65d86c2 | |
renovate | 08dcc897cc | |
renovate | c975fb0fee | |
Adrian Simmons | 350c4260c2 | |
drone | b73cf344b6 | |
kolaente | d4b45dc255 | |
kolaente | e2beaba3b9 | |
renovate | dd9be97793 |
|
@ -99,7 +99,7 @@ steps:
|
|||
- dependencies
|
||||
|
||||
- name: test-frontend
|
||||
image: cypress/browsers:node14.17.0-chrome91-ff89
|
||||
image: cypress/browsers:node16.5.0-chrome94-ff93
|
||||
pull: true
|
||||
environment:
|
||||
CYPRESS_API_URL: http://api:3456/api/v1
|
||||
|
|
|
@ -8,7 +8,7 @@ const testAndAssertFailed = fixture => {
|
|||
|
||||
cy.wait(5000) // It can take waaaayy too long to log the user in
|
||||
cy.url().should('include', '/')
|
||||
cy.get('div.notification.is-danger').contains('Wrong username or password.')
|
||||
cy.get('div.message.danger').contains('Wrong username or password.')
|
||||
}
|
||||
|
||||
context('Login', () => {
|
||||
|
|
|
@ -32,7 +32,7 @@ context('Registration', () => {
|
|||
cy.get('h2').should('contain', `Hi ${fixture.username}!`)
|
||||
})
|
||||
|
||||
it('Should fail', () => {
|
||||
it.only('Should fail', () => {
|
||||
const fixture = {
|
||||
username: 'test',
|
||||
password: '123456',
|
||||
|
@ -45,6 +45,6 @@ context('Registration', () => {
|
|||
cy.get('#password').type(fixture.password)
|
||||
cy.get('#passwordValidation').type(fixture.password)
|
||||
cy.get('#register-submit').click()
|
||||
cy.get('div.notification.is-danger').contains('A user with this username already exists.')
|
||||
cy.get('div.message.danger').contains('A user with this username already exists.')
|
||||
})
|
||||
})
|
53
package.json
53
package.json
|
@ -17,17 +17,17 @@
|
|||
"browserslist:update": "npx browserslist@latest --update-db"
|
||||
},
|
||||
"dependencies": {
|
||||
"@bulvar/bulma": "^0.9.3",
|
||||
"@bulvar/bulma": "^0.9.4",
|
||||
"@github/hotkey": "1.6.0",
|
||||
"@kyvg/vue3-notification": "2.3.4",
|
||||
"@sentry/tracing": "6.15.0",
|
||||
"@sentry/vue": "6.15.0",
|
||||
"@vue/compat": "3.2.22",
|
||||
"@vueuse/core": "7.0.1",
|
||||
"@vue/compat": "3.2.23",
|
||||
"@vueuse/core": "7.1.2",
|
||||
"camel-case": "4.1.2",
|
||||
"codemirror": "5.64.0",
|
||||
"copy-to-clipboard": "3.3.1",
|
||||
"date-fns": "2.26.0",
|
||||
"date-fns": "2.27.0",
|
||||
"dompurify": "2.3.3",
|
||||
"easymde": "2.15.0",
|
||||
"flatpickr": "4.6.9",
|
||||
|
@ -36,57 +36,58 @@
|
|||
"is-touch-device": "1.0.1",
|
||||
"lodash.clonedeep": "4.5.0",
|
||||
"lodash.debounce": "4.0.8",
|
||||
"marked": "4.0.4",
|
||||
"marked": "4.0.5",
|
||||
"register-service-worker": "1.7.2",
|
||||
"snake-case": "3.0.4",
|
||||
"ufo": "0.7.9",
|
||||
"v-tooltip": "4.0.0-beta.2",
|
||||
"vue": "3.2.22",
|
||||
"vue": "3.2.23",
|
||||
"vue-advanced-cropper": "2.7.0",
|
||||
"vue-drag-resize": "2.0.3",
|
||||
"vue-flatpickr-component": "9.0.5",
|
||||
"vue-i18n": "9.2.0-beta.20",
|
||||
"vue-i18n": "9.2.0-beta.22",
|
||||
"vue-router": "4.0.12",
|
||||
"vuedraggable": "4.1.0",
|
||||
"vuex": "4.0.2",
|
||||
"workbox-precaching": "6.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@4tw/cypress-drag-drop": "2.0.0",
|
||||
"@4tw/cypress-drag-drop": "2.1.0",
|
||||
"@fortawesome/fontawesome-svg-core": "1.2.36",
|
||||
"@fortawesome/free-regular-svg-icons": "5.15.4",
|
||||
"@fortawesome/free-solid-svg-icons": "5.15.4",
|
||||
"@fortawesome/vue-fontawesome": "3.0.0-5",
|
||||
"@types/flexsearch": "0.7.2",
|
||||
"@types/jest": "27.0.3",
|
||||
"@typescript-eslint/eslint-plugin": "5.4.0",
|
||||
"@typescript-eslint/parser": "5.4.0",
|
||||
"@typescript-eslint/eslint-plugin": "5.5.0",
|
||||
"@typescript-eslint/parser": "5.5.0",
|
||||
"@vitejs/plugin-legacy": "1.6.3",
|
||||
"@vitejs/plugin-vue": "1.10.0",
|
||||
"@vitejs/plugin-vue": "1.10.1",
|
||||
"@vue/eslint-config-typescript": "9.1.0",
|
||||
"autoprefixer": "10.4.0",
|
||||
"axios": "0.24.0",
|
||||
"browserslist": "4.18.1",
|
||||
"cypress": "8.7.0",
|
||||
"cypress-file-upload": "5.0.8",
|
||||
"esbuild": "0.13.15",
|
||||
"esbuild": "0.14.1",
|
||||
"eslint": "8.3.0",
|
||||
"eslint-plugin-vue": "8.1.1",
|
||||
"express": "4.17.1",
|
||||
"faker": "5.5.3",
|
||||
"jest": "27.3.1",
|
||||
"netlify-cli": "7.1.0",
|
||||
"postcss": "8.3.11",
|
||||
"jest": "27.4.2",
|
||||
"netlify-cli": "8.0.6",
|
||||
"postcss": "8.4.4",
|
||||
"postcss-preset-env": "7.0.1",
|
||||
"rollup": "2.60.1",
|
||||
"rollup": "2.60.2",
|
||||
"rollup-plugin-visualizer": "5.5.2",
|
||||
"sass": "1.43.4",
|
||||
"sass": "1.44.0",
|
||||
"slugify": "1.6.3",
|
||||
"ts-jest": "27.0.7",
|
||||
"typescript": "4.5.2",
|
||||
"vite": "2.6.14",
|
||||
"vite-plugin-pwa": "0.11.7",
|
||||
"vite-plugin-pwa": "0.11.9",
|
||||
"vite-svg-loader": "3.1.0",
|
||||
"vue-tsc": "0.29.6",
|
||||
"vue-tsc": "0.29.8",
|
||||
"wait-on": "6.0.0",
|
||||
"workbox-cli": "6.4.1"
|
||||
},
|
||||
|
@ -95,7 +96,8 @@
|
|||
"env": {
|
||||
"browser": true,
|
||||
"es2021": true,
|
||||
"node": true
|
||||
"node": true,
|
||||
"vue/setup-compiler-macros": true
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
|
@ -119,6 +121,7 @@
|
|||
"error",
|
||||
"never"
|
||||
],
|
||||
"vue/script-setup-uses-vars": "error",
|
||||
"vue/multi-word-component-names": 0
|
||||
},
|
||||
"parser": "vue-eslint-parser",
|
||||
|
@ -129,7 +132,10 @@
|
|||
"ignorePatterns": [
|
||||
"*.test.*",
|
||||
"cypress/*"
|
||||
]
|
||||
],
|
||||
"globals": {
|
||||
"defineProps": "readonly"
|
||||
}
|
||||
},
|
||||
"postcss": {
|
||||
"plugins": {
|
||||
|
@ -154,5 +160,6 @@
|
|||
"json"
|
||||
]
|
||||
},
|
||||
"license": "AGPL-3.0-or-later"
|
||||
}
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"packageManager": "yarn@1.22.17"
|
||||
}
|
|
@ -82,7 +82,7 @@ export default {
|
|||
this.$route.name === 'labels.index' ||
|
||||
this.$route.name === 'migrate.start' ||
|
||||
this.$route.name === 'migrate.wunderlist' ||
|
||||
this.$route.name === 'user.settings' ||
|
||||
this.$route.name.startsWith('user.settings') ||
|
||||
this.$route.name === 'namespaces.index'
|
||||
) {
|
||||
return this.$store.dispatch(CURRENT_LIST, null)
|
||||
|
|
|
@ -30,27 +30,25 @@
|
|||
<a @click="() => (configureApi = true)">{{ $t('apiConfig.change') }}</a>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="notification is-success mt-2"
|
||||
v-if="successMsg !== '' && errorMsg === ''"
|
||||
>
|
||||
<message variant="success" v-if="successMsg !== '' && errorMsg === ''" class="mt-2">
|
||||
{{ successMsg }}
|
||||
</div>
|
||||
<div
|
||||
class="notification is-danger mt-2"
|
||||
v-if="errorMsg !== '' && successMsg === ''"
|
||||
>
|
||||
</message>
|
||||
<message variant="danger" v-if="errorMsg !== '' && successMsg === ''" class="mt-2">
|
||||
{{ errorMsg }}
|
||||
</div>
|
||||
</message>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Message from '@/components/misc/message'
|
||||
import {parseURL} from 'ufo'
|
||||
import {checkAndSetApiUrl} from '@/helpers/checkAndSetApiUrl'
|
||||
|
||||
export default {
|
||||
name: 'apiConfig',
|
||||
components: {
|
||||
Message,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
configureApi: false,
|
||||
|
|
|
@ -1,15 +1,18 @@
|
|||
<template>
|
||||
<div class="notification is-danger">
|
||||
<message variant="danger">
|
||||
<i18n-t keypath="loadingError.failed">
|
||||
<a @click="reload">{{ $t('loadingError.tryAgain') }}</a>
|
||||
<a href="https://vikunja.io/contact/" rel="noreferrer noopener nofollow" target="_blank">{{ $t('loadingError.contact') }}</a>
|
||||
</i18n-t>
|
||||
</div>
|
||||
</message>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Message from '@/components/misc/message'
|
||||
|
||||
export default {
|
||||
name: 'error',
|
||||
components: {Message},
|
||||
methods: {
|
||||
reload() {
|
||||
window.location.reload()
|
||||
|
|
|
@ -4,13 +4,11 @@
|
|||
<template v-for="(s, i) in shortcuts" :key="i">
|
||||
<h3>{{ $t(s.title) }}</h3>
|
||||
|
||||
<div class="message is-primary">
|
||||
<div class="message-body">
|
||||
{{
|
||||
s.available($route) ? $t('keyboardShortcuts.currentPageOnly') : $t('keyboardShortcuts.allPages')
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
<message>
|
||||
{{
|
||||
s.available($route) ? $t('keyboardShortcuts.currentPageOnly') : $t('keyboardShortcuts.allPages')
|
||||
}}
|
||||
</message>
|
||||
|
||||
<dl class="shortcut-list">
|
||||
<template v-for="(sc, si) in s.shortcuts" :key="si">
|
||||
|
@ -30,11 +28,15 @@
|
|||
<script>
|
||||
import {KEYBOARD_SHORTCUTS_ACTIVE} from '@/store/mutation-types'
|
||||
import Shortcut from '@/components/misc/shortcut.vue'
|
||||
import Message from '@/components/misc/message'
|
||||
import {KEYBOARD_SHORTCUTS} from './shortcuts'
|
||||
|
||||
export default {
|
||||
name: 'keyboard-shortcuts',
|
||||
components: {Shortcut},
|
||||
components: {
|
||||
Message,
|
||||
Shortcut,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
shortcuts: KEYBOARD_SHORTCUTS,
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
<template>
|
||||
<div class="message" :class="variant">
|
||||
<slot/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
defineProps({
|
||||
variant: {
|
||||
type: String,
|
||||
default: 'info',
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.message {
|
||||
padding: .75rem 1rem;
|
||||
border-radius: $radius;
|
||||
}
|
||||
|
||||
.info {
|
||||
border: 1px solid var(--primary);
|
||||
background: hsla(var(--primary-hsl), .05);
|
||||
}
|
||||
|
||||
.danger {
|
||||
border: 1px solid var(--danger);
|
||||
background: hsla(var(--danger-h), var(--danger-s), var(--danger-l), .05);
|
||||
}
|
||||
|
||||
.warning {
|
||||
border: 1px solid var(--warning);
|
||||
background: hsla(var(--warning-h), var(--warning-s), var(--warning-l), .05);
|
||||
}
|
||||
|
||||
.success {
|
||||
border: 1px solid var(--success);
|
||||
background: hsla(var(--success-h), var(--success-s), var(--success-l), .05);
|
||||
}
|
||||
</style>
|
|
@ -2,14 +2,9 @@
|
|||
<div class="no-auth-wrapper">
|
||||
<div class="noauth-container">
|
||||
<Logo class="logo" width="400" height="117" />
|
||||
<div class="message is-info" v-if="motd !== ''">
|
||||
<div class="message-header">
|
||||
<p>{{ $t('misc.info') }}</p>
|
||||
</div>
|
||||
<div class="message-body">
|
||||
{{ motd }}
|
||||
</div>
|
||||
</div>
|
||||
<message v-if="motd !== ''" class="my-2">
|
||||
{{ motd }}
|
||||
</message>
|
||||
<slot/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -17,6 +12,7 @@
|
|||
|
||||
<script setup>
|
||||
import Logo from '@/components/home/Logo.vue'
|
||||
import message from '@/components/misc/message'
|
||||
import {useStore} from 'vuex'
|
||||
import {computed} from 'vue'
|
||||
|
||||
|
@ -26,7 +22,7 @@ const motd = computed(() => store.state.config.motd)
|
|||
|
||||
<style lang="scss" scoped>
|
||||
.no-auth-wrapper {
|
||||
background: url('@/assets/llama.svg') no-repeat bottom left fixed var(--site-background);
|
||||
background: url('@/assets/llama.svg?url') no-repeat bottom left fixed var(--site-background);
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
|
@ -39,5 +35,6 @@ const motd = computed(() => store.state.config.motd)
|
|||
|
||||
.logo {
|
||||
color: var(--logo-text-color);
|
||||
max-width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
<p v-if="error === errorNoApiUrl">
|
||||
{{ $t('ready.noApiUrlConfigured') }}
|
||||
</p>
|
||||
<div class="notification is-danger" v-else>
|
||||
<message variant="danger" v-else>
|
||||
<p>
|
||||
{{ $t('ready.errorOccured') }}<br/>
|
||||
{{ error }}
|
||||
|
@ -24,7 +24,7 @@
|
|||
<p>
|
||||
{{ $t('ready.checkApiUrl') }}
|
||||
</p>
|
||||
</div>
|
||||
</message>
|
||||
<api-config :configure-open="true" @found-api="load"/>
|
||||
</card>
|
||||
</no-auth-wrapper>
|
||||
|
@ -43,6 +43,7 @@
|
|||
<script>
|
||||
import Logo from '@/assets/logo.svg?component'
|
||||
import ApiConfig from '@/components/misc/api-config'
|
||||
import Message from '@/components/misc/message'
|
||||
import NoAuthWrapper from '@/components/misc/no-auth-wrapper'
|
||||
import {mapState} from 'vuex'
|
||||
import {ERROR_NO_API_URL} from '@/helpers/checkAndSetApiUrl'
|
||||
|
@ -50,6 +51,7 @@ import {ERROR_NO_API_URL} from '@/helpers/checkAndSetApiUrl'
|
|||
export default {
|
||||
name: 'ready',
|
||||
components: {
|
||||
Message,
|
||||
Logo,
|
||||
NoAuthWrapper,
|
||||
ApiConfig,
|
||||
|
|
|
@ -193,10 +193,6 @@ export default {
|
|||
align-items: center;
|
||||
|
||||
}
|
||||
|
||||
.message-body {
|
||||
padding: .5rem .75rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,17 +3,13 @@
|
|||
<div class="field is-grouped">
|
||||
<p class="control has-icons-left is-expanded">
|
||||
<textarea
|
||||
:disabled="taskService.loading || null"
|
||||
class="input"
|
||||
:disabled="taskService.loading || undefined"
|
||||
class="add-task-textarea input"
|
||||
:placeholder="$t('list.list.addPlaceholder')"
|
||||
cols="1"
|
||||
rows="1"
|
||||
v-focus
|
||||
v-model="newTaskTitle"
|
||||
ref="newTaskInput"
|
||||
:style="{
|
||||
'minHeight': `${initialTextAreaHeight}px`,
|
||||
'height': `calc(${textAreaHeight}px - 2px + 1rem)`
|
||||
}"
|
||||
@keyup="errorMessage = ''"
|
||||
@keydown.enter="handleEnter"
|
||||
/>
|
||||
|
@ -23,7 +19,8 @@
|
|||
</p>
|
||||
<p class="control">
|
||||
<x-button
|
||||
:disabled="newTaskTitle === '' || taskService.loading || null"
|
||||
class="add-task-button"
|
||||
:disabled="newTaskTitle === '' || taskService.loading || undefined"
|
||||
@click="addTask()"
|
||||
icon="plus"
|
||||
:loading="taskService.loading"
|
||||
|
@ -35,121 +32,172 @@
|
|||
<p class="help is-danger" v-if="errorMessage !== ''">
|
||||
{{ errorMessage }}
|
||||
</p>
|
||||
<quick-add-magic v-if="errorMessage === ''"/>
|
||||
<quick-add-magic v-else />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup lang="ts">
|
||||
import {ref, watch, unref, shallowReactive} from 'vue'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
import {useStore} from 'vuex'
|
||||
import { tryOnMounted, debouncedWatch, useWindowSize, MaybeRef } from '@vueuse/core'
|
||||
|
||||
import TaskService from '../../services/task'
|
||||
import QuickAddMagic from '@/components/tasks/partials/quick-add-magic.vue'
|
||||
|
||||
const INPUT_BORDER_PX = 2
|
||||
const LINE_HEIGHT = 1.5 // using getComputedStyles().lineHeight returns an (wrong) absolute pixel value, we need the factor to do calculations with it.
|
||||
|
||||
const cleanupTitle = title => {
|
||||
function cleanupTitle(title: string) {
|
||||
return title.replace(/^((\* |\+ |- )(\[ \] )?)/g, '')
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'add-task',
|
||||
emits: ['taskAdded'],
|
||||
data() {
|
||||
return {
|
||||
newTaskTitle: '',
|
||||
taskService: new TaskService(),
|
||||
errorMessage: '',
|
||||
textAreaHeight: null,
|
||||
initialTextAreaHeight: null,
|
||||
function useAutoHeightTextarea(value: MaybeRef<string>) {
|
||||
const textarea = ref<HTMLInputElement>()
|
||||
const minHeight = ref(0)
|
||||
|
||||
// adapted from https://github.com/LeaVerou/stretchy/blob/47f5f065c733029acccb755cae793009645809e2/src/stretchy.js#L34
|
||||
function resize(textareaEl: HTMLInputElement|undefined) {
|
||||
if (!textareaEl) return
|
||||
|
||||
let empty
|
||||
|
||||
// the value here is the the attribute value
|
||||
if (!textareaEl.value && textareaEl.placeholder) {
|
||||
empty = true
|
||||
textareaEl.value = textareaEl.placeholder
|
||||
}
|
||||
},
|
||||
components: {
|
||||
QuickAddMagic,
|
||||
},
|
||||
props: {
|
||||
defaultPosition: {
|
||||
type: Number,
|
||||
required: false,
|
||||
|
||||
const cs = getComputedStyle(textareaEl)
|
||||
|
||||
textareaEl.style.minHeight = ''
|
||||
textareaEl.style.height = '0'
|
||||
const offset = textareaEl.offsetHeight - parseFloat(cs.paddingTop) - parseFloat(cs.paddingBottom)
|
||||
const height = textareaEl.scrollHeight + offset + 'px'
|
||||
|
||||
textareaEl.style.height = height
|
||||
|
||||
// calculate min-height for the first time
|
||||
if (!minHeight.value) {
|
||||
minHeight.value = parseFloat(height)
|
||||
}
|
||||
|
||||
textareaEl.style.minHeight = minHeight.value.toString()
|
||||
|
||||
|
||||
if (empty) {
|
||||
textareaEl.value = ''
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
tryOnMounted(() => {
|
||||
if (textarea.value) {
|
||||
// we don't want scrollbars
|
||||
textarea.value.style.overflowY = 'hidden'
|
||||
}
|
||||
})
|
||||
|
||||
const { width: windowWidth } = useWindowSize()
|
||||
|
||||
debouncedWatch(
|
||||
windowWidth,
|
||||
() => resize(textarea.value),
|
||||
{ debounce: 200 },
|
||||
)
|
||||
|
||||
// It is not possible to get notified of a change of the value attribute of a textarea without workarounds (setTimeout)
|
||||
// So instead we watch the value that we bound to it.
|
||||
watch(
|
||||
() => [textarea.value, unref(value)],
|
||||
() => resize(textarea.value),
|
||||
{
|
||||
immediate: true, // calculate initial size
|
||||
flush: 'post', // resize after value change is rendered to DOM
|
||||
},
|
||||
)
|
||||
|
||||
return textarea
|
||||
}
|
||||
|
||||
const emit = defineEmits(['taskAdded'])
|
||||
|
||||
|
||||
const props = defineProps({
|
||||
defaultPosition: {
|
||||
type: Number,
|
||||
required: false,
|
||||
},
|
||||
watch: {
|
||||
newTaskTitle(newVal) {
|
||||
// Calculating the textarea height based on lines of input in it.
|
||||
// That is more reliable when removing a line from the input.
|
||||
const numberOfLines = newVal.split(/\r\n|\r|\n/).length
|
||||
const fontSize = parseFloat(window.getComputedStyle(this.$refs.newTaskInput, null).getPropertyValue('font-size'))
|
||||
})
|
||||
|
||||
this.textAreaHeight = numberOfLines * fontSize * LINE_HEIGHT + INPUT_BORDER_PX
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.initialTextAreaHeight = this.$refs.newTaskInput.scrollHeight + INPUT_BORDER_PX
|
||||
},
|
||||
methods: {
|
||||
async addTask() {
|
||||
if (this.newTaskTitle === '') {
|
||||
this.errorMessage = this.$t('list.create.addTitleRequired')
|
||||
return
|
||||
}
|
||||
this.errorMessage = ''
|
||||
const taskService = shallowReactive(new TaskService())
|
||||
const errorMessage = ref('')
|
||||
|
||||
if (this.taskService.loading) {
|
||||
return
|
||||
}
|
||||
const newTaskTitle = ref('')
|
||||
const newTaskInput = useAutoHeightTextarea(newTaskTitle)
|
||||
|
||||
const newTasks = this.newTaskTitle.split(/[\r\n]+/).map(async t => {
|
||||
const title = cleanupTitle(t)
|
||||
if (title === '') {
|
||||
return
|
||||
}
|
||||
const { t } = useI18n()
|
||||
const store = useStore()
|
||||
|
||||
const task = await this.$store.dispatch('tasks/createNewTask', {
|
||||
title,
|
||||
listId: this.$store.state.auth.settings.defaultListId,
|
||||
position: this.defaultPosition,
|
||||
})
|
||||
this.$emit('taskAdded', task)
|
||||
return task
|
||||
})
|
||||
async function addTask() {
|
||||
if (newTaskTitle.value === '') {
|
||||
errorMessage.value = t('list.create.addTitleRequired')
|
||||
return
|
||||
}
|
||||
errorMessage.value = ''
|
||||
|
||||
try {
|
||||
await Promise.all(newTasks)
|
||||
this.newTaskTitle = ''
|
||||
} catch (e) {
|
||||
if (e.message === 'NO_LIST') {
|
||||
this.errorMessage = this.$t('list.create.addListRequired')
|
||||
return
|
||||
}
|
||||
throw e
|
||||
}
|
||||
},
|
||||
handleEnter(e) {
|
||||
// when pressing shift + enter we want to continue as we normally would. Otherwise, we want to create
|
||||
// the new task(s). The vue event modifier don't allow this, hence this method.
|
||||
if (e.shiftKey) {
|
||||
return
|
||||
}
|
||||
if (taskService.loading) {
|
||||
return
|
||||
}
|
||||
|
||||
e.preventDefault()
|
||||
this.addTask()
|
||||
},
|
||||
},
|
||||
const taskTitleBackup = newTaskTitle.value
|
||||
const newTasks = newTaskTitle.value.split(/[\r\n]+/).map(async uncleanedTitle => {
|
||||
const title = cleanupTitle(uncleanedTitle)
|
||||
if (title === '') {
|
||||
return
|
||||
}
|
||||
|
||||
const task = await store.dispatch('tasks/createNewTask', {
|
||||
title,
|
||||
listId: store.state.auth.settings.defaultListId,
|
||||
position: props.defaultPosition,
|
||||
})
|
||||
emit('taskAdded', task)
|
||||
return task
|
||||
})
|
||||
|
||||
try {
|
||||
newTaskTitle.value = ''
|
||||
await Promise.all(newTasks)
|
||||
} catch (e: any) {
|
||||
newTaskTitle.value = taskTitleBackup
|
||||
if (e?.message === 'NO_LIST') {
|
||||
errorMessage.value = t('list.create.addListRequired')
|
||||
return
|
||||
}
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
function handleEnter(e: KeyboardEvent) {
|
||||
// when pressing shift + enter we want to continue as we normally would. Otherwise, we want to create
|
||||
// the new task(s). The vue event modifier don't allow this, hence this method.
|
||||
if (e.shiftKey) {
|
||||
return
|
||||
}
|
||||
|
||||
e.preventDefault()
|
||||
addTask()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.task-add {
|
||||
margin-bottom: 0;
|
||||
|
||||
.button {
|
||||
height: 2.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.input, .textarea {
|
||||
.add-task-button {
|
||||
height: 2.5rem;
|
||||
}
|
||||
.add-task-textarea {
|
||||
transition: border-color $transition;
|
||||
}
|
||||
|
||||
.input {
|
||||
resize: vertical;
|
||||
resize: none;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -2,13 +2,6 @@
|
|||
<div class="gantt-chart">
|
||||
<div class="filter-container">
|
||||
<div class="items">
|
||||
<x-button
|
||||
@click.prevent.stop="showTaskFilter = !showTaskFilter"
|
||||
type="secondary"
|
||||
icon="filter"
|
||||
>
|
||||
{{ $t('filters.title') }}
|
||||
</x-button>
|
||||
<filter-popup
|
||||
:visible="showTaskFilter"
|
||||
v-model="params"
|
||||
|
@ -237,7 +230,6 @@ export default {
|
|||
newTaskFieldActive: false,
|
||||
priorities: priorities,
|
||||
taskCollectionService: new TaskCollectionService(),
|
||||
showTaskFilter: false,
|
||||
|
||||
params: {
|
||||
sort_by: ['done', 'id'],
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import {computed, watch, readonly} from 'vue'
|
||||
import {useStorage, createSharedComposable, ColorSchemes, usePreferredColorScheme, tryOnMounted} from '@vueuse/core'
|
||||
import {useStorage, createSharedComposable, ColorSchema, usePreferredColorScheme, tryOnMounted} from '@vueuse/core'
|
||||
|
||||
const STORAGE_KEY = 'color-scheme'
|
||||
|
||||
const DEFAULT_COLOR_SCHEME_SETTING: ColorSchemes = 'light'
|
||||
const DEFAULT_COLOR_SCHEME_SETTING: ColorSchema = 'light'
|
||||
|
||||
const CLASS_DARK = 'dark'
|
||||
const CLASS_LIGHT = 'light'
|
||||
|
@ -16,7 +16,7 @@ const CLASS_LIGHT = 'light'
|
|||
// - value is synced via `createSharedComposable`
|
||||
// https://github.com/vueuse/vueuse/blob/main/packages/core/useDark/index.ts
|
||||
export const useColorScheme = createSharedComposable(() => {
|
||||
const store = useStorage<ColorSchemes>(STORAGE_KEY, DEFAULT_COLOR_SCHEME_SETTING)
|
||||
const store = useStorage<ColorSchema>(STORAGE_KEY, DEFAULT_COLOR_SCHEME_SETTING)
|
||||
|
||||
const preferredColorScheme = usePreferredColorScheme()
|
||||
|
||||
|
|
|
@ -901,7 +901,7 @@
|
|||
"5010": "Tento tým nemá k tomuto prostoru přístup.",
|
||||
"5011": "Tento uživatel již má přístup k tomuto prostoru.",
|
||||
"5012": "Prostor je archivován, a proto je přístupný pouze pro čtení.",
|
||||
"6001": "Název týmu nemůže být prázdný.",
|
||||
"6001": "The team name cannot be empty.",
|
||||
"6002": "Tým neexistuje.",
|
||||
"6004": "Tým již má přístup k tomuto prostoru nebo seznamu.",
|
||||
"6005": "Uživatel je již členem tohoto týmu.",
|
||||
|
|
|
@ -114,12 +114,12 @@
|
|||
"vikunja": "Vikunja"
|
||||
},
|
||||
"appearance": {
|
||||
"title": "Color Scheme",
|
||||
"setSuccess": "Saved change of color scheme to {colorScheme}",
|
||||
"title": "Farbschema",
|
||||
"setSuccess": "Änderung des Farbschemas auf {colorScheme} gespeichert",
|
||||
"colorScheme": {
|
||||
"light": "Light",
|
||||
"light": "Hell",
|
||||
"system": "System",
|
||||
"dark": "Dark"
|
||||
"dark": "Dunkel"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -556,7 +556,7 @@
|
|||
"text2": "Dies wird auch alle Anhänge, Erinnerungen und Verknüpfungen, die zu dieser Aufgabe gehören löschen und kann nicht rückgängig gemacht werden!"
|
||||
},
|
||||
"actions": {
|
||||
"assign": "Assign to a user",
|
||||
"assign": "Benutzer:in zuweisen",
|
||||
"label": "Label hinzufügen",
|
||||
"priority": "Priorität setzen",
|
||||
"dueDate": "Fälligkeitsdatum setzen",
|
||||
|
@ -775,7 +775,7 @@
|
|||
"task": {
|
||||
"title": "Aufgabenseite",
|
||||
"done": "Eine Aufgabe als erledigt markieren",
|
||||
"assign": "Assign to a user",
|
||||
"assign": "Benutzer:in zuweisen",
|
||||
"labels": "Dieser Aufgabe ein Label hinzufügen",
|
||||
"dueDate": "Ändere das Fälligkeitsdatum dieser Aufgabe",
|
||||
"attachment": "Einen Anhang dieser Aufgabe hinzufügen",
|
||||
|
|
|
@ -114,12 +114,12 @@
|
|||
"vikunja": "Vikunja"
|
||||
},
|
||||
"appearance": {
|
||||
"title": "Color Scheme",
|
||||
"setSuccess": "Saved change of color scheme to {colorScheme}",
|
||||
"title": "Farbschema",
|
||||
"setSuccess": "Änderung des Farbschemas auf {colorScheme} gespeichert",
|
||||
"colorScheme": {
|
||||
"light": "Light",
|
||||
"light": "Hell",
|
||||
"system": "System",
|
||||
"dark": "Dark"
|
||||
"dark": "Dunkel"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -556,7 +556,7 @@
|
|||
"text2": "Das wird au alli Ahhäng, Errinnerige und Beziehige wo mit dere Uufgab verchnüpft sind chüble und cha nid rückgängig gmacht werde!"
|
||||
},
|
||||
"actions": {
|
||||
"assign": "Assign to a user",
|
||||
"assign": "Benutzer:in zuweisen",
|
||||
"label": "Label hinzuefüege",
|
||||
"priority": "Priorität setzä",
|
||||
"dueDate": "Fälligkeitsdatum setze",
|
||||
|
@ -775,7 +775,7 @@
|
|||
"task": {
|
||||
"title": "Uufgabesiite",
|
||||
"done": "Uufgab als erledigt markiere",
|
||||
"assign": "Assign to a user",
|
||||
"assign": "Benutzer:in zuweisen",
|
||||
"labels": "Labels ennere Uufgab hinzuefüege",
|
||||
"dueDate": "S'Fälligkeitsdatum für die Uufgab ändere",
|
||||
"attachment": "En Aahang dere Uufgab hinzuefüege",
|
||||
|
@ -901,7 +901,7 @@
|
|||
"5010": "Da Team hett kei zuegriff uf de Namensruum.",
|
||||
"5011": "De Benutzer hett bereits zuegriff uf de Namensruum.",
|
||||
"5012": "De Namensruum isch momentan schriibgschützt weil er archiviert isch.",
|
||||
"6001": "Der TeamName kann nicht leer sein.",
|
||||
"6001": "Der Teamname kann nicht leer sein.",
|
||||
"6002": "Da Team giz nid.",
|
||||
"6004": "Da Team het scho Zuegang zu dem Namensruum oder Liste.",
|
||||
"6005": "De Benutzer isch scho bi dem Team.",
|
||||
|
|
|
@ -901,7 +901,7 @@
|
|||
"5010": "This team does not have access to that namespace.",
|
||||
"5011": "This user has already access to that namespace.",
|
||||
"5012": "The namespace is archived and can therefore only be accessed read only.",
|
||||
"6001": "The team name cannot be emtpy.",
|
||||
"6001": "The team name cannot be empty.",
|
||||
"6002": "The team does not exist.",
|
||||
"6004": "The team already has access to that namespace or list.",
|
||||
"6005": "The user is already a member of that team.",
|
||||
|
|
|
@ -901,7 +901,7 @@
|
|||
"5010": "This team does not have access to that namespace.",
|
||||
"5011": "This user has already access to that namespace.",
|
||||
"5012": "The namespace is archived and can therefore only be accessed read only.",
|
||||
"6001": "The team name cannot be emtpy.",
|
||||
"6001": "The team name cannot be empty.",
|
||||
"6002": "The team does not exist.",
|
||||
"6004": "The team already has access to that namespace or list.",
|
||||
"6005": "The user is already a member of that team.",
|
||||
|
|
|
@ -901,7 +901,7 @@
|
|||
"5010": "Cette équipe n’a pas accès à cet espace de noms.",
|
||||
"5011": "Cet·e utilisateur·rice a déjà accès à cet espace de noms.",
|
||||
"5012": "L’espace de noms est archivé et ne peut donc être consulté qu’en lecture seule.",
|
||||
"6001": "Le nom de l’équipe ne peut pas être vide.",
|
||||
"6001": "The team name cannot be empty.",
|
||||
"6002": "L’équipe n’existe pas.",
|
||||
"6004": "L’équipe a déjà accès à cet espace de noms ou à cette liste.",
|
||||
"6005": "L’utilisateur·rice est déjà membre de cette équipe.",
|
||||
|
|
|
@ -901,7 +901,7 @@
|
|||
"5010": "This team does not have access to that namespace.",
|
||||
"5011": "This user has already access to that namespace.",
|
||||
"5012": "The namespace is archived and can therefore only be accessed read only.",
|
||||
"6001": "The team name cannot be emtpy.",
|
||||
"6001": "The team name cannot be empty.",
|
||||
"6002": "The team does not exist.",
|
||||
"6004": "The team already has access to that namespace or list.",
|
||||
"6005": "The user is already a member of that team.",
|
||||
|
|
|
@ -901,7 +901,7 @@
|
|||
"5010": "This team does not have access to that namespace.",
|
||||
"5011": "This user has already access to that namespace.",
|
||||
"5012": "The namespace is archived and can therefore only be accessed read only.",
|
||||
"6001": "The team name cannot be emtpy.",
|
||||
"6001": "The team name cannot be empty.",
|
||||
"6002": "The team does not exist.",
|
||||
"6004": "The team already has access to that namespace or list.",
|
||||
"6005": "The user is already a member of that team.",
|
||||
|
|
|
@ -901,7 +901,7 @@
|
|||
"5010": "This team does not have access to that namespace.",
|
||||
"5011": "This user has already access to that namespace.",
|
||||
"5012": "The namespace is archived and can therefore only be accessed read only.",
|
||||
"6001": "The team name cannot be emtpy.",
|
||||
"6001": "The team name cannot be empty.",
|
||||
"6002": "The team does not exist.",
|
||||
"6004": "The team already has access to that namespace or list.",
|
||||
"6005": "The user is already a member of that team.",
|
||||
|
|
|
@ -901,7 +901,7 @@
|
|||
"5010": "This team does not have access to that namespace.",
|
||||
"5011": "This user has already access to that namespace.",
|
||||
"5012": "The namespace is archived and can therefore only be accessed read only.",
|
||||
"6001": "The team name cannot be emtpy.",
|
||||
"6001": "The team name cannot be empty.",
|
||||
"6002": "The team does not exist.",
|
||||
"6004": "The team already has access to that namespace or list.",
|
||||
"6005": "The user is already a member of that team.",
|
||||
|
|
|
@ -901,7 +901,7 @@
|
|||
"5010": "This team does not have access to that namespace.",
|
||||
"5011": "This user has already access to that namespace.",
|
||||
"5012": "The namespace is archived and can therefore only be accessed read only.",
|
||||
"6001": "The team name cannot be emtpy.",
|
||||
"6001": "The team name cannot be empty.",
|
||||
"6002": "The team does not exist.",
|
||||
"6004": "The team already has access to that namespace or list.",
|
||||
"6005": "The user is already a member of that team.",
|
||||
|
|
|
@ -901,7 +901,7 @@
|
|||
"5010": "У этой команды нет доступа к этому пространству имён.",
|
||||
"5011": "Этот пользователь уже имеет доступ к этому пространству имён.",
|
||||
"5012": "Это пространство имён архивировано и поэтому доступно только для чтения.",
|
||||
"6001": "Имя команды не может быть пустым.",
|
||||
"6001": "The team name cannot be empty.",
|
||||
"6002": "Команда не существует.",
|
||||
"6004": "Эта команда уже имеет доступ к этому пространству имён или списку.",
|
||||
"6005": "Пользователь уже является участником этой команды.",
|
||||
|
|
|
@ -0,0 +1,933 @@
|
|||
{
|
||||
"home": {
|
||||
"welcomeNight": "Good Night {username}",
|
||||
"welcomeMorning": "Good Morning {username}",
|
||||
"welcomeDay": "Hi {username}",
|
||||
"welcomeEvening": "Good Evening {username}",
|
||||
"lastViewed": "Last viewed",
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"title": "Not found",
|
||||
"text": "The page you requested does not exist."
|
||||
},
|
||||
"ready": {
|
||||
"loading": "Vikunja is loading…",
|
||||
"errorOccured": "An error occured:",
|
||||
"checkApiUrl": "Please check if the api url is correct.",
|
||||
"noApiUrlConfigured": "No API url was configured. Please set one below:"
|
||||
},
|
||||
"offline": {
|
||||
"title": "You are offline.",
|
||||
"text": "Please check your network connection and try again."
|
||||
},
|
||||
"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!",
|
||||
"openIdGeneralError": "An error occured while authenticating against the third party.",
|
||||
"logout": "Logout"
|
||||
},
|
||||
"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.",
|
||||
"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"
|
||||
},
|
||||
"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."
|
||||
},
|
||||
"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"
|
||||
},
|
||||
"avatar": {
|
||||
"title": "Avatar",
|
||||
"initials": "Initials",
|
||||
"gravatar": "Gravatar",
|
||||
"upload": "Upload",
|
||||
"uploadAvatar": "Upload Avatar",
|
||||
"statusUpdateSuccess": "Avatar status was updated successfully!",
|
||||
"setSuccess": "The avatar has been set successfully!"
|
||||
},
|
||||
"quickAddMagic": {
|
||||
"title": "Quick Add Magic Mode",
|
||||
"disabled": "Disabled",
|
||||
"todoist": "Todoist",
|
||||
"vikunja": "Vikunja"
|
||||
},
|
||||
"appearance": {
|
||||
"title": "Color Scheme",
|
||||
"setSuccess": "Saved change of color scheme to {colorScheme}",
|
||||
"colorScheme": {
|
||||
"light": "Light",
|
||||
"system": "System",
|
||||
"dark": "Dark"
|
||||
}
|
||||
}
|
||||
},
|
||||
"deletion": {
|
||||
"title": "Delete your Vikunja Account",
|
||||
"text1": "The deletion of your account is permanent and cannot be undone. We will delete all your namespaces, lists, tasks and everything associated with it.",
|
||||
"text2": "To proceed, please enter your password. You will receive an email with further instructions.",
|
||||
"confirm": "Delete my account",
|
||||
"requestSuccess": "The request was successful. You'll receive an email with further instructions.",
|
||||
"passwordRequired": "Please enter your password.",
|
||||
"confirmSuccess": "You've successfully confirmed the deletion of your account. We will delete your account in three days.",
|
||||
"scheduled": "We will delete your Vikunja account at {date} ({dateSince}).",
|
||||
"scheduledCancel": "To cancel the deletion of your account, click here.",
|
||||
"scheduledCancelText": "To cancel the deletion of your account, please enter your password below:",
|
||||
"scheduledCancelConfirm": "Cancel the deletion of my account",
|
||||
"scheduledCancelSuccess": "We will not delete your account."
|
||||
},
|
||||
"export": {
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"list": {
|
||||
"archived": "This list is archived. It is not possible to create new or edit tasks for it.",
|
||||
"title": "List Title",
|
||||
"color": "Color",
|
||||
"lists": "Lists",
|
||||
"search": "Type to search for a list…",
|
||||
"searchSelect": "Click or press enter to select this list",
|
||||
"shared": "Shared Lists",
|
||||
"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."
|
||||
},
|
||||
"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."
|
||||
},
|
||||
"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!"
|
||||
},
|
||||
"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."
|
||||
},
|
||||
"duplicate": {
|
||||
"title": "Duplicate this list",
|
||||
"label": "Duplicate",
|
||||
"text": "Select a namespace which should hold the duplicated list:",
|
||||
"success": "The list was successfully duplicated."
|
||||
},
|
||||
"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…",
|
||||
"color": "Color",
|
||||
"success": "The list was successfully updated."
|
||||
},
|
||||
"share": {
|
||||
"header": "Share this list",
|
||||
"title": "Share \"{list}\"",
|
||||
"share": "Share",
|
||||
"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)",
|
||||
"namePlaceholder": "e.g. Lorem Ipsum",
|
||||
"nameExplanation": "All actions done by this link share will show up with the name.",
|
||||
"password": "Password (optional)",
|
||||
"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"
|
||||
},
|
||||
"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."
|
||||
},
|
||||
"right": {
|
||||
"title": "Right",
|
||||
"read": "Read only",
|
||||
"readWrite": "Read & write",
|
||||
"admin": "Admin"
|
||||
},
|
||||
"attributes": {
|
||||
"link": "Link",
|
||||
"name": "Name",
|
||||
"sharedBy": "Shared by",
|
||||
"right": "Right",
|
||||
"delete": "Delete"
|
||||
}
|
||||
},
|
||||
"list": {
|
||||
"title": "List",
|
||||
"add": "Add",
|
||||
"addPlaceholder": "Add a new task…",
|
||||
"empty": "This list is currently empty.",
|
||||
"newTaskCta": "Create a new task.",
|
||||
"editTask": "Edit Task"
|
||||
},
|
||||
"gantt": {
|
||||
"title": "Gantt",
|
||||
"showTasksWithoutDates": "Show tasks which don't have dates set",
|
||||
"size": "Size",
|
||||
"default": "Default",
|
||||
"month": "Month",
|
||||
"day": "Day",
|
||||
"from": "From",
|
||||
"to": "To",
|
||||
"noDates": "This task has no dates set."
|
||||
},
|
||||
"table": {
|
||||
"title": "Table",
|
||||
"columns": "Columns"
|
||||
},
|
||||
"kanban": {
|
||||
"title": "Kanban",
|
||||
"limit": "Limit: {limit}",
|
||||
"noLimit": "Not Set",
|
||||
"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.",
|
||||
"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",
|
||||
"addBucket": "Create a new bucket",
|
||||
"addBucketPlaceholder": "Enter the new bucket title…",
|
||||
"deleteHeaderBucket": "Delete the bucket",
|
||||
"deleteBucketText1": "Are you sure you want to delete this bucket?",
|
||||
"deleteBucketText2": "This will not delete any tasks but move them into the default bucket.",
|
||||
"deleteBucketSuccess": "The bucket has been deleted successfully.",
|
||||
"bucketTitleSavedSuccess": "The bucket title has been saved successfully.",
|
||||
"bucketLimitSavedSuccess": "The bucket limit been saved successfully.",
|
||||
"collapse": "Collapse this bucket"
|
||||
},
|
||||
"pseudo": {
|
||||
"favorites": {
|
||||
"title": "Favorites"
|
||||
}
|
||||
}
|
||||
},
|
||||
"namespace": {
|
||||
"title": "Namespaces & Lists",
|
||||
"namespace": "Namespace",
|
||||
"showArchived": "Show Archived",
|
||||
"noneAvailable": "You don't have any namespaces right now.",
|
||||
"unarchive": "Un-Archive",
|
||||
"archived": "Archived",
|
||||
"noLists": "This namespace does not contain any lists.",
|
||||
"createList": "Create a new list in this namespace.",
|
||||
"namespaces": "Namespaces",
|
||||
"search": "Type to search for a namespace…",
|
||||
"create": {
|
||||
"title": "Create a new namespace",
|
||||
"titleRequired": "Please specify a title.",
|
||||
"explanation": "A namespace is a collection of lists you can share and use to organize your lists with. In fact, every list belongs to a namepace.",
|
||||
"tooltip": "What's a namespace?",
|
||||
"success": "The namespace was successfully created."
|
||||
},
|
||||
"archive": {
|
||||
"titleArchive": "Archive \"{namespace}\"",
|
||||
"titleUnarchive": "Un-Archive \"{namespace}\"",
|
||||
"archiveText": "You won't be able to edit this namespace or create new lists until you un-archive it. This will also archive all lists in this namespace.",
|
||||
"unarchiveText": "You will be able to create new lists or edit it.",
|
||||
"success": "The namespace was successfully archived.",
|
||||
"description": "If a namespace is archived, you cannot create new lists or edit it."
|
||||
},
|
||||
"delete": {
|
||||
"title": "Delete \"{namespace}\"",
|
||||
"text1": "Are you sure you want to delete this namespace and all of its contents?",
|
||||
"text2": "This includes all lists and tasks and CANNOT BE UNDONE!",
|
||||
"success": "The namespace was successfully deleted."
|
||||
},
|
||||
"edit": {
|
||||
"title": "Edit \"{namespace}\"",
|
||||
"success": "The namespace was successfully updated."
|
||||
},
|
||||
"share": {
|
||||
"title": "Share \"{namespace}\""
|
||||
},
|
||||
"attributes": {
|
||||
"title": "Namespace Title",
|
||||
"titlePlaceholder": "The namespace title goes here…",
|
||||
"description": "Description",
|
||||
"descriptionPlaceholder": "The namespaces description goes here…",
|
||||
"color": "Color",
|
||||
"archived": "Is Archived",
|
||||
"isArchived": "This namespace is archived"
|
||||
},
|
||||
"pseudo": {
|
||||
"sharedLists": {
|
||||
"title": "Shared Lists"
|
||||
},
|
||||
"favorites": {
|
||||
"title": "Favorites"
|
||||
},
|
||||
"savedFilters": {
|
||||
"title": "Filters"
|
||||
}
|
||||
}
|
||||
},
|
||||
"filters": {
|
||||
"title": "Filters",
|
||||
"clear": "Clear Filters",
|
||||
"attributes": {
|
||||
"title": "Title",
|
||||
"titlePlaceholder": "The saved filter title goes here…",
|
||||
"description": "Description",
|
||||
"descriptionPlaceholder": "The description goes here…",
|
||||
"includeNulls": "Include Tasks which don't have a value set",
|
||||
"requireAll": "Require all filters to be true for a task to show up",
|
||||
"showDoneTasks": "Show Done Tasks",
|
||||
"enablePriority": "Enable Filter By Priority",
|
||||
"enablePercentDone": "Enable Filter By Percent Done",
|
||||
"dueDateRange": "Due Date Range",
|
||||
"startDateRange": "Start Date Range",
|
||||
"endDateRange": "End Date Range",
|
||||
"reminderRange": "Reminder Date Range"
|
||||
},
|
||||
"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.",
|
||||
"action": "Create new saved filter"
|
||||
},
|
||||
"delete": {
|
||||
"header": "Delete this saved filter",
|
||||
"text": "Are you sure you want to delete this saved filter?",
|
||||
"success": "The filter was deleted successfully."
|
||||
},
|
||||
"edit": {
|
||||
"title": "Edit This Saved Filter",
|
||||
"success": "The filter was saved successfully."
|
||||
}
|
||||
},
|
||||
"migrate": {
|
||||
"title": "Migrate from other services to Vikunja",
|
||||
"titleService": "Import your data from {name} into Vikunja",
|
||||
"import": "Import your data into Vikunja",
|
||||
"description": "Click on the logo of one of the third-party services below to get started.",
|
||||
"descriptionDo": "Vikunja will import all lists, tasks, notes, reminders and files you have access to.",
|
||||
"authorize": "To authorize Vikunja to access your {name} Account, click the button below.",
|
||||
"getStarted": "Get Started",
|
||||
"inProgress": "Importing in progress…",
|
||||
"alreadyMigrated1": "It looks like you've already imported your stuff from {name} at {date}.",
|
||||
"alreadyMigrated2": "Importing again is possible, but might create duplicates. Are you sure?",
|
||||
"confirm": "I am sure, please start migrating now!",
|
||||
"importUpload": "To import data from {name} into Vikunja, click the button below to select a file.",
|
||||
"upload": "Upload file"
|
||||
},
|
||||
"label": {
|
||||
"title": "Labels",
|
||||
"manage": "Manage labels",
|
||||
"description": "Click on a label to edit it. You can edit all labels you created, you can use all labels which are associated with a task to whose list you have access.",
|
||||
"newCTA": "You currently do not have any labels.",
|
||||
"search": "Type to search for a label…",
|
||||
"create": {
|
||||
"header": "New label",
|
||||
"title": "Create a new label",
|
||||
"titleRequired": "Please specify a title.",
|
||||
"success": "The label was successfully created."
|
||||
},
|
||||
"edit": {
|
||||
"header": "Edit Label",
|
||||
"forbidden": "You are not allowed to edit this label because you dont own it.",
|
||||
"success": "The label was successfully updated."
|
||||
},
|
||||
"deleteSuccess": "The label was successfully deleted.",
|
||||
"attributes": {
|
||||
"title": "Title",
|
||||
"titlePlaceholder": "The label title goes here…",
|
||||
"description": "Description",
|
||||
"descriptionPlaceholder": "Label description",
|
||||
"color": "Color"
|
||||
}
|
||||
},
|
||||
"sharing": {
|
||||
"authenticating": "Authenticating…",
|
||||
"passwordRequired": "This shared list requires a password. Please enter it below:",
|
||||
"error": "An error occured.",
|
||||
"invalidPassword": "The password is invalid."
|
||||
},
|
||||
"navigation": {
|
||||
"overview": "Overview",
|
||||
"upcoming": "Upcoming",
|
||||
"settings": "Settings",
|
||||
"imprint": "Imprint",
|
||||
"privacy": "Privacy Policy"
|
||||
},
|
||||
"misc": {
|
||||
"loading": "Loading…",
|
||||
"save": "Save",
|
||||
"delete": "Delete",
|
||||
"confirm": "Confirm",
|
||||
"cancel": "Cancel",
|
||||
"refresh": "Refresh",
|
||||
"disable": "Disable",
|
||||
"copy": "Copy to clipboard",
|
||||
"search": "Search",
|
||||
"searchPlaceholder": "Type to search…",
|
||||
"previous": "Previous",
|
||||
"next": "Next",
|
||||
"poweredBy": "Powered by Vikunja",
|
||||
"info": "Info",
|
||||
"create": "Create",
|
||||
"doit": "Do it!",
|
||||
"saving": "Saving…",
|
||||
"saved": "Saved!",
|
||||
"default": "Default",
|
||||
"close": "Close",
|
||||
"download": "Download",
|
||||
"showMenu": "Show the menu",
|
||||
"hideMenu": "Hide the menu"
|
||||
},
|
||||
"input": {
|
||||
"resetColor": "Reset Color",
|
||||
"datepicker": {
|
||||
"today": "Today",
|
||||
"tomorrow": "Tomorrow",
|
||||
"nextMonday": "Next Monday",
|
||||
"thisWeekend": "This Weekend",
|
||||
"laterThisWeek": "Later This Week",
|
||||
"nextWeek": "Next Week",
|
||||
"chooseDate": "Choose a date"
|
||||
},
|
||||
"editor": {
|
||||
"edit": "Edit",
|
||||
"done": "Done",
|
||||
"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",
|
||||
"unorderedList": "Unordered List",
|
||||
"orderedList": "Ordered List",
|
||||
"cleanBlock": "Clean Block",
|
||||
"link": "Link",
|
||||
"image": "Image",
|
||||
"table": "Table",
|
||||
"horizontalRule": "Horizontal Rule",
|
||||
"sideBySide": "Side By Side",
|
||||
"guide": "Guide"
|
||||
},
|
||||
"multiselect": {
|
||||
"createPlaceholder": "Create new",
|
||||
"selectPlaceholder": "Click or press enter to select"
|
||||
}
|
||||
},
|
||||
"task": {
|
||||
"task": "Task",
|
||||
"new": "Create a new task",
|
||||
"delete": "Delete this task",
|
||||
"createSuccess": "The task was successfully created.",
|
||||
"addReminder": "Add a new reminder…",
|
||||
"doneSuccess": "The task was successfully marked as done.",
|
||||
"undoneSuccess": "The task was successfully un-marked as done.",
|
||||
"openDetail": "Open task detail view",
|
||||
"checklistTotal": "{checked} of {total} tasks",
|
||||
"checklistAllDone": "{total} tasks",
|
||||
"show": {
|
||||
"titleCurrent": "Current Tasks",
|
||||
"titleDates": "Tasks from {from} until {to}",
|
||||
"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!"
|
||||
},
|
||||
"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!",
|
||||
"undone": "Mark as undone",
|
||||
"created": "Created {0} by {1}",
|
||||
"updated": "Updated {0}",
|
||||
"doneAt": "Done {0}",
|
||||
"updateSuccess": "The task was saved successfully.",
|
||||
"deleteSuccess": "The task has been deleted successfully.",
|
||||
"belongsToList": "This task belongs to list '{list}'",
|
||||
"due": "Due {at}",
|
||||
"closePopup": "Close popup",
|
||||
"delete": {
|
||||
"header": "Delete this task",
|
||||
"text1": "Are you sure you want to remove this task?",
|
||||
"text2": "This will also remove all attachments, reminders and relations associated with this task and cannot be undone!"
|
||||
},
|
||||
"actions": {
|
||||
"assign": "Assign to a user",
|
||||
"label": "Add labels",
|
||||
"priority": "Set Priority",
|
||||
"dueDate": "Set Due Date",
|
||||
"startDate": "Set a Start Date",
|
||||
"endDate": "Set an End Date",
|
||||
"reminders": "Set Reminders",
|
||||
"repeatAfter": "Set a repeating interval",
|
||||
"percentDone": "Set Percent Done",
|
||||
"attachments": "Add attachments",
|
||||
"relatedTasks": "Add task relations",
|
||||
"moveList": "Move task",
|
||||
"color": "Set task color",
|
||||
"delete": "Delete task",
|
||||
"favorite": "Save as favorite",
|
||||
"unfavorite": "Remove from favorites"
|
||||
}
|
||||
},
|
||||
"attributes": {
|
||||
"assignees": "Assignees",
|
||||
"color": "Color",
|
||||
"created": "Created",
|
||||
"createdBy": "Created By",
|
||||
"description": "Description",
|
||||
"done": "Done",
|
||||
"dueDate": "Due Date",
|
||||
"endDate": "End Date",
|
||||
"labels": "Labels",
|
||||
"percentDone": "% Done",
|
||||
"priority": "Priority",
|
||||
"relatedTasks": "Related Tasks",
|
||||
"reminders": "Reminders",
|
||||
"repeat": "Repeat",
|
||||
"startDate": "Start Date",
|
||||
"title": "Title",
|
||||
"updated": "Updated"
|
||||
},
|
||||
"subscription": {
|
||||
"subscribedThroughParent": "You can't unsubscribe here because you are subscribed to this {entity} through its {parent}.",
|
||||
"subscribed": "You are currently subscribed to this {entity} and will receive notifications for changes.",
|
||||
"notSubscribed": "You are not subscribed to this {entity} and won't receive notifications for changes.",
|
||||
"subscribe": "Subscribe",
|
||||
"unsubscribe": "Unsubscribe",
|
||||
"subscribeSuccess": "You are now subscribed to this {entity}",
|
||||
"unsubscribeSuccess": "You are now unsubscribed to this {entity}"
|
||||
},
|
||||
"attachment": {
|
||||
"title": "Attachments",
|
||||
"createdBy": "created {0} by {1}",
|
||||
"downloadTooltip": "Download this attachment",
|
||||
"upload": "Upload attachment",
|
||||
"drop": "Drop files here to upload",
|
||||
"delete": "Delete attachment",
|
||||
"deleteTooltip": "Delete this attachment",
|
||||
"deleteText1": "Are you sure you want to delete the attachment {filename}?",
|
||||
"deleteText2": "This cannot be undone!",
|
||||
"copyUrl": "Copy URL",
|
||||
"copyUrlTooltip": "Copy the url of this attachment for usage in text"
|
||||
},
|
||||
"comment": {
|
||||
"title": "Comments",
|
||||
"loading": "Loading comments…",
|
||||
"edited": "edited {date}",
|
||||
"creating": "Creating comment…",
|
||||
"placeholder": "Add your comment…",
|
||||
"comment": "Comment",
|
||||
"delete": "Delete this comment",
|
||||
"deleteText1": "Are you sure you want to delete this comment?",
|
||||
"deleteText2": "This cannot be undone!",
|
||||
"addedSuccess": "The comment was added successfully."
|
||||
},
|
||||
"deferDueDate": {
|
||||
"title": "Defer due date",
|
||||
"1day": "1 day",
|
||||
"3days": "3 days",
|
||||
"1week": "1 week"
|
||||
},
|
||||
"description": {
|
||||
"placeholder": "Click here to enter a description…",
|
||||
"empty": "No description available yet."
|
||||
},
|
||||
"assignee": {
|
||||
"placeholder": "Type to assign a user…",
|
||||
"selectPlaceholder": "Assign this user",
|
||||
"assignSuccess": "The user has been assigned successfully.",
|
||||
"unassignSuccess": "The user has been unassigned successfully."
|
||||
},
|
||||
"label": {
|
||||
"placeholder": "Type to add a new label…",
|
||||
"createPlaceholder": "Add this as new label",
|
||||
"addSuccess": "The label has been added successfully.",
|
||||
"createSuccess": "The label has been created successfully.",
|
||||
"removeSuccess": "The label has been removed successfully.",
|
||||
"addCreateSuccess": "The label has been created and added successfully."
|
||||
},
|
||||
"priority": {
|
||||
"unset": "Unset",
|
||||
"low": "Low",
|
||||
"medium": "Medium",
|
||||
"high": "high",
|
||||
"urgent": "Urgent",
|
||||
"doNow": "DO NOW"
|
||||
},
|
||||
"relation": {
|
||||
"add": "Add a New Task Relation",
|
||||
"new": "New Task Relation",
|
||||
"searchPlaceholder": "Type search for a new task to add as related…",
|
||||
"createPlaceholder": "Add this as new related task",
|
||||
"differentList": "This task belongs to a different list.",
|
||||
"differentNamespace": "This task belongs to a different namespace.",
|
||||
"noneYet": "No task relations yet.",
|
||||
"delete": "Delete Task Relation",
|
||||
"deleteText1": "Are you sure you want to delete this task relation?",
|
||||
"deleteText2": "This cannot be undone!",
|
||||
"select": "Select a relation kind",
|
||||
"kinds": {
|
||||
"subtask": "Subtask | Subtasks",
|
||||
"parenttask": "Parent Task | Parent Tasks",
|
||||
"related": "Related Task | Related Tasks",
|
||||
"duplicateof": "Duplicate Of | Duplicates Of",
|
||||
"duplicates": "Duplicates | Duplicates",
|
||||
"blocking": "Blocking | Blocking",
|
||||
"blocked": "Blocked By | Blocked By",
|
||||
"precedes": "Precedes | Precedes",
|
||||
"follows": "Follows | Follows",
|
||||
"copiedfrom": "Copied From | Copied From",
|
||||
"copiedto": "Copied To | Copied To"
|
||||
}
|
||||
},
|
||||
"repeat": {
|
||||
"everyDay": "Every Day",
|
||||
"everyWeek": "Every Week",
|
||||
"everyMonth": "Every Month",
|
||||
"mode": "Repeat mode",
|
||||
"monthly": "Monthly",
|
||||
"fromCurrentDate": "From Current Date",
|
||||
"each": "Each",
|
||||
"specifyAmount": "Specify an amount…",
|
||||
"hours": "Hours",
|
||||
"days": "Days",
|
||||
"weeks": "Weeks",
|
||||
"months": "Months",
|
||||
"years": "Years"
|
||||
},
|
||||
"quickAddMagic": {
|
||||
"hint": "You can use Quick Add Magic",
|
||||
"what": "What?",
|
||||
"title": "Quick Add Magic",
|
||||
"intro": "When creating a task, you can use special keywords to directly add attributes to the newly created task. This allows to add commonly used attributes to tasks much faster.",
|
||||
"multiple": "You can use this multiple times.",
|
||||
"label1": "To add a label, simply prefix the name of the label with {prefix}.",
|
||||
"label2": "Vikunja will first check if the label already exist and create it if not.",
|
||||
"label3": "To use spaces, simply add a \" around the label name.",
|
||||
"label4": "For example: {prefix}\"Label with spaces\".",
|
||||
"priority1": "To set a task's priority, add a number 1-5, prefixed with a {prefix}.",
|
||||
"priority2": "The higher the number, the higher the priority.",
|
||||
"assignees": "To directly assign the task to a user, add their username prefixed with {prefix} to the task.",
|
||||
"list1": "To set a list for the task to appear in, enter its name prefixed with {prefix}.",
|
||||
"list2": "This will return an error if the list does not exist.",
|
||||
"dateAndTime": "Date and time",
|
||||
"date": "Any date will be used as the due date of the new task. You can use dates in any of these formats:",
|
||||
"dateWeekday": "any weekday, will use the next date with that date",
|
||||
"dateCurrentYear": "will use the current year",
|
||||
"dateNth": "will use the {day}th of the current month",
|
||||
"dateTime": "Combine any of the date formats with \"{time}\" (or {timePM}) to set a time."
|
||||
}
|
||||
},
|
||||
"team": {
|
||||
"title": "Teams",
|
||||
"noTeams": "You are currently not part of any teams.",
|
||||
"create": {
|
||||
"title": "Create a new team",
|
||||
"success": "The team was successfully created."
|
||||
},
|
||||
"edit": {
|
||||
"title": "Edit Team \"{team}\"",
|
||||
"members": "Team Members",
|
||||
"search": "Type to search a user…",
|
||||
"addUser": "Add to team",
|
||||
"makeMember": "Make Member",
|
||||
"makeAdmin": "Make Admin",
|
||||
"success": "The team was successfully updated.",
|
||||
"userAddedSuccess": "The team member was successfully added.",
|
||||
"madeMember": "The team member was successfully made member.",
|
||||
"madeAdmin": "The team member was successfully made admin.",
|
||||
"delete": {
|
||||
"header": "Delete the team",
|
||||
"text1": "Are you sure you want to delete this team and all of its members?",
|
||||
"text2": "All team members will lose access to lists and namespaces shared with this team. This CANNOT BE UNDONE!",
|
||||
"success": "The team was successfully deleted."
|
||||
},
|
||||
"deleteUser": {
|
||||
"header": "Remove a user from the team",
|
||||
"text1": "Are you sure you want to remove this user from the team?",
|
||||
"text2": "They will lose access to all lists and namespaces this team has access to. This CANNOT BE UNDONE!",
|
||||
"success": "The user was successfully deleted from the team."
|
||||
}
|
||||
},
|
||||
"attributes": {
|
||||
"name": "Team Name",
|
||||
"namePlaceholder": "The team's name goes here…",
|
||||
"nameRequired": "Please specify a name.",
|
||||
"description": "Description",
|
||||
"descriptionPlaceholder": "The teams description goes here…",
|
||||
"admin": "Admin",
|
||||
"member": "Member"
|
||||
}
|
||||
},
|
||||
"keyboardShortcuts": {
|
||||
"title": "Keyboard Shortcuts",
|
||||
"general": "General",
|
||||
"allPages": "These shortcuts work on all pages.",
|
||||
"currentPageOnly": "These shortcuts work only on the current page.",
|
||||
"toggleMenu": "Toggle The Menu",
|
||||
"quickSearch": "Open the search/quick action bar",
|
||||
"then": "then",
|
||||
"task": {
|
||||
"title": "Task Page",
|
||||
"done": "Mark a task as done",
|
||||
"assign": "Assign to a user",
|
||||
"labels": "Add labels to this task",
|
||||
"dueDate": "Change the due date of this task",
|
||||
"attachment": "Add an attachment to this task",
|
||||
"related": "Modify related tasks of this task"
|
||||
},
|
||||
"list": {
|
||||
"title": "List Views",
|
||||
"switchToListView": "Switch to list view",
|
||||
"switchToGanttView": "Switch to gantt view",
|
||||
"switchToKanbanView": "Switch to kanban view",
|
||||
"switchToTableView": "Switch to table view"
|
||||
}
|
||||
},
|
||||
"update": {
|
||||
"available": "There is an update for Vikunja available!",
|
||||
"do": "Update Now"
|
||||
},
|
||||
"menu": {
|
||||
"edit": "Edit",
|
||||
"archive": "Archive",
|
||||
"duplicate": "Duplicate",
|
||||
"delete": "Delete",
|
||||
"unarchive": "Un-Archive",
|
||||
"setBackground": "Set background",
|
||||
"share": "Share",
|
||||
"newList": "New list"
|
||||
},
|
||||
"apiConfig": {
|
||||
"url": "Vikunja URL",
|
||||
"urlPlaceholder": "eg. https://localhost:3456",
|
||||
"change": "change",
|
||||
"signInOn": "Sign in to your Vikunja account on {0}",
|
||||
"error": "Could not find or use Vikunja installation at \"{domain}\". Please try a different url.",
|
||||
"success": "Using Vikunja installation at \"{domain}\".",
|
||||
"urlRequired": "A url is required."
|
||||
},
|
||||
"loadingError": {
|
||||
"failed": "Loading failed, please {0}. If the error persists, please {1}.",
|
||||
"tryAgain": "try again",
|
||||
"contact": "contact us"
|
||||
},
|
||||
"notification": {
|
||||
"title": "Notifications",
|
||||
"none": "You don't have any notifications. Have a nice day!",
|
||||
"explainer": "Notifications will appear here when actions on namespaces, lists or tasks you subscribed to happen."
|
||||
},
|
||||
"quickActions": {
|
||||
"commands": "Commands",
|
||||
"placeholder": "Type a command or search…",
|
||||
"hint": "You can use {list} to limit the search to a list. Combine {list} or {label} (labels) with a search query to search for a task with these labels or on that list. Use {assignee} to only search for teams.",
|
||||
"tasks": "Tasks",
|
||||
"lists": "Lists",
|
||||
"teams": "Teams",
|
||||
"newList": "Enter the title of the new list…",
|
||||
"newTask": "Enter the title of the new task…",
|
||||
"newNamespace": "Enter the title of the new namespace…",
|
||||
"newTeam": "Enter the name of the new team…",
|
||||
"createTask": "Create a task in the current list ({title})",
|
||||
"createList": "Create a list in the current namespace ({title})",
|
||||
"cmds": {
|
||||
"newTask": "New task",
|
||||
"newList": "New list",
|
||||
"newNamespace": "New namespace",
|
||||
"newTeam": "New team"
|
||||
}
|
||||
},
|
||||
"date": {
|
||||
"locale": "en",
|
||||
"altFormatLong": "j M Y H:i",
|
||||
"altFormatShort": "j M Y"
|
||||
},
|
||||
"error": {
|
||||
"error": "Error",
|
||||
"success": "Success",
|
||||
"0001": "You're not allowed to do that.",
|
||||
"1001": "A user with this username already exists.",
|
||||
"1002": "A user with this email address already exists.",
|
||||
"1004": "No username and password specified.",
|
||||
"1005": "The user does not exist.",
|
||||
"1006": "Could not get the user id.",
|
||||
"1008": "No password reset token provided.",
|
||||
"1009": "Invalid password reset token.",
|
||||
"1010": "Invalid email confirm token.",
|
||||
"1011": "Wrong username or password.",
|
||||
"1012": "Email address of the user not confirmed.",
|
||||
"1013": "New password is empty.",
|
||||
"1014": "Old password is empty.",
|
||||
"1015": "Totp is already enabled for this user.",
|
||||
"1016": "Totp is not enabled for this user.",
|
||||
"1017": "The totp passcode is invalid.",
|
||||
"1018": "The user avatar type setting is invalid.",
|
||||
"2001": "ID cannot be empty or 0.",
|
||||
"2002": "Some of the request data was invalid.",
|
||||
"3001": "The list does not exist.",
|
||||
"3004": "You need to have read permissions on that list to perform that action.",
|
||||
"3005": "The list title cannot be empty.",
|
||||
"3006": "The list share does not exist.",
|
||||
"3007": "A list with this identifier already exists.",
|
||||
"3008": "The list is archived and can therefore only be accessed read only. This is also true for all tasks associated with this list.",
|
||||
"4001": "The list task text cannot be empty.",
|
||||
"4002": "The list task does not exist.",
|
||||
"4003": "All bulk editing tasks must belong to the same list.",
|
||||
"4004": "Need at least one task when bulk editing tasks.",
|
||||
"4005": "You do not have the right to see the task.",
|
||||
"4006": "You can't set a parent task as the task itself.",
|
||||
"4007": "You can't create a task relation with an invalid kind of relation.",
|
||||
"4008": "You can't create a task relation which already exists.",
|
||||
"4009": "The task relation does not exist.",
|
||||
"4010": "Cannot relate a task with itself.",
|
||||
"4011": "The task attachment does not exist.",
|
||||
"4012": "The task attachment is too large.",
|
||||
"4013": "The task sort param is invalid.",
|
||||
"4014": "The task sort order is invalid.",
|
||||
"4015": "The task comment does not exist.",
|
||||
"4016": "Invalid task field.",
|
||||
"4017": "Invalid task filter comparator.",
|
||||
"4018": "Invalid task filter concatinator.",
|
||||
"4019": "Invalid task filter value.",
|
||||
"5001": "The namespace does not exist.",
|
||||
"5003": "You do not have access to the specified namespace.",
|
||||
"5006": "The namespace name cannot be empty.",
|
||||
"5009": "You need to have namespace read access to perform that action.",
|
||||
"5010": "This team does not have access to that namespace.",
|
||||
"5011": "This user has already access to that namespace.",
|
||||
"5012": "The namespace is archived and can therefore only be accessed read only.",
|
||||
"6001": "The team name cannot be empty.",
|
||||
"6002": "The team does not exist.",
|
||||
"6004": "The team already has access to that namespace or list.",
|
||||
"6005": "The user is already a member of that team.",
|
||||
"6006": "Cannot delete the last team member.",
|
||||
"6007": "The team does not have access to the list to perform that action.",
|
||||
"7002": "The user already has access to that list.",
|
||||
"7003": "You do not have access to that list.",
|
||||
"8001": "This label already exists on that task.",
|
||||
"8002": "The label does not exist.",
|
||||
"8003": "You do not have access to this label.",
|
||||
"9001": "The right is invalid.",
|
||||
"10001": "The bucket does not exist.",
|
||||
"10002": "The bucket does not belong to that list.",
|
||||
"10003": "You cannot remove the last bucket on a list.",
|
||||
"10004": "You cannot add the task to this bucket as it already exceeded the limit of tasks it can hold.",
|
||||
"10005": "There can be only one done bucket per list.",
|
||||
"11001": "The saved filter does not exist.",
|
||||
"11002": "Saved filters are not available for link shares.",
|
||||
"12001": "The subscription entity type is invalid.",
|
||||
"12002": "You are already subscribed to the entity itself or a parent entity.",
|
||||
"13001": "This link share requires a password for authentication, but none was provided.",
|
||||
"13002": "The provided link share password was invalid."
|
||||
},
|
||||
"about": {
|
||||
"title": "About",
|
||||
"frontendVersion": "Frontend Version: {version}",
|
||||
"apiVersion": "API Version: {version}"
|
||||
}
|
||||
}
|
|
@ -901,7 +901,7 @@
|
|||
"5010": "This team does not have access to that namespace.",
|
||||
"5011": "This user has already access to that namespace.",
|
||||
"5012": "The namespace is archived and can therefore only be accessed read only.",
|
||||
"6001": "The team name cannot be emtpy.",
|
||||
"6001": "The team name cannot be empty.",
|
||||
"6002": "The team does not exist.",
|
||||
"6004": "The team already has access to that namespace or list.",
|
||||
"6005": "The user is already a member of that team.",
|
||||
|
|
|
@ -114,12 +114,12 @@
|
|||
"vikunja": "Vikunja"
|
||||
},
|
||||
"appearance": {
|
||||
"title": "Color Scheme",
|
||||
"setSuccess": "Saved change of color scheme to {colorScheme}",
|
||||
"title": "Phối màu",
|
||||
"setSuccess": "Đã lưu phối màu vào {colorScheme}",
|
||||
"colorScheme": {
|
||||
"light": "Light",
|
||||
"system": "System",
|
||||
"dark": "Dark"
|
||||
"light": "Sáng",
|
||||
"system": "Hệ thống",
|
||||
"dark": "Tối"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -556,7 +556,7 @@
|
|||
"text2": "Thao tác này cũng sẽ xóa tất cả tệp đính kèm, lời nhắc và liên kết đến công việc này. Nó không thể hoàn tác!"
|
||||
},
|
||||
"actions": {
|
||||
"assign": "Assign to a user",
|
||||
"assign": "Chỉ định một người",
|
||||
"label": "Thêm nhãn",
|
||||
"priority": "Mức độ ưu tiên",
|
||||
"dueDate": "Đặt ngày đến hạn",
|
||||
|
@ -775,7 +775,7 @@
|
|||
"task": {
|
||||
"title": "Trang công việc",
|
||||
"done": "Đánh dấu hoàn thành",
|
||||
"assign": "Assign to a user",
|
||||
"assign": "Chỉ định một người",
|
||||
"labels": "Thêm nhãn cho công việc này",
|
||||
"dueDate": "Thay đổi ngày hết hạn của công việc này",
|
||||
"attachment": "Thêm tệp đính kèm cho công việc này",
|
||||
|
@ -901,7 +901,7 @@
|
|||
"5010": "Team này không có quyền bước vào góc làm việc đó.",
|
||||
"5011": "Người này đã có quyền bước vào góc làm việc đó.",
|
||||
"5012": "Góc làm việc đã được lưu trữ nên chỉ có thể vào đó để đọc.",
|
||||
"6001": "Team cũng cần có tên gọi mà.",
|
||||
"6001": "The team name cannot be empty.",
|
||||
"6002": "Team không tồn tại.",
|
||||
"6004": "Team đã có quyền bước vào góc làm việc và xem danh sách.",
|
||||
"6005": "Người này đã là thành viên của Team đó rồi.",
|
||||
|
|
|
@ -19,14 +19,12 @@ $mobile: math.div($tablet, 2);
|
|||
$family-sans-serif: 'Open Sans', Helvetica, Arial, sans-serif;
|
||||
$vikunja-font: 'Quicksand', sans-serif;
|
||||
|
||||
$thickness: 1px;
|
||||
$pagination-current-border: var(--primary);
|
||||
$navbar-item-active-color: var(--primary);
|
||||
|
||||
$dropdown-content-shadow: none;
|
||||
$dropdown-item-hover-background-color: var(--grey-100);
|
||||
|
||||
$bulmaswatch-import-font: false !default;
|
||||
$site-background: var(--grey-100);
|
||||
|
||||
$transition-duration: 150ms;
|
||||
|
|
|
@ -79,6 +79,8 @@
|
|||
--link: var(--primary);
|
||||
--link-hover: hsla(var(--primary-h), var(--primary-s), var(--primary-l), .75);
|
||||
--border: var(--grey-200);
|
||||
--input-disabled-background-color: var(--grey-100);
|
||||
--input-disabled-border-color: var(--grey-300);
|
||||
|
||||
&.dark {
|
||||
// Light mode colours reversed for dark mode
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
@import "@bulvar/bulma/sass/elements/content";
|
||||
@import "@bulvar/bulma/sass/elements/icon";
|
||||
@import "@bulvar/bulma/sass/elements/image";
|
||||
@import "@bulvar/bulma/sass/elements/notification";
|
||||
// @import "@bulvar/bulma/sass/elements/notification"; // not used
|
||||
@import "@bulvar/bulma/sass/elements/progress";
|
||||
@import "@bulvar/bulma/sass/elements/table";
|
||||
@import "@bulvar/bulma/sass/elements/tag";
|
||||
|
@ -48,7 +48,7 @@
|
|||
// @import "@bulvar/bulma/sass/components/level"; // not used
|
||||
@import "@bulvar/bulma/sass/components/media";
|
||||
@import "@bulvar/bulma/sass/components/menu";
|
||||
@import "@bulvar/bulma/sass/components/message";
|
||||
// @import "@bulvar/bulma/sass/components/message"; //not used
|
||||
@import "@bulvar/bulma/sass/components/modal";
|
||||
@import "@bulvar/bulma/sass/components/navbar";
|
||||
@import "@bulvar/bulma/sass/components/pagination";
|
||||
|
|
|
@ -7,4 +7,4 @@
|
|||
@import "form";
|
||||
@import "link-share";
|
||||
@import "loading";
|
||||
@import "notification";
|
||||
@import "flatpickr";
|
|
@ -0,0 +1,221 @@
|
|||
// Flatpickr overrides to use css custom properties and enable styling it
|
||||
|
||||
.flatpickr-calendar {
|
||||
background: var(--white);
|
||||
box-shadow: 1px 0 0 var(--grey-200), -1px 0 0 var(--grey-200), 0 1px 0 var(--grey-200), 0 -1px 0 var(--grey-200), 0 3px 13px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.flatpickr-calendar.multiMonth .flatpickr-days .dayContainer:nth-child(n+2) .flatpickr-day.inRange:nth-child(7n+1) {
|
||||
box-shadow: -2px 0 0 var(--grey-200), 5px 0 0 var(--grey-200);
|
||||
}
|
||||
|
||||
.flatpickr-calendar.hasTime .flatpickr-time {
|
||||
border-top: 1px solid var(--grey-200);
|
||||
}
|
||||
|
||||
.flatpickr-calendar.arrowTop:before {
|
||||
border-bottom-color: var(--grey-200);
|
||||
}
|
||||
|
||||
.flatpickr-calendar.arrowTop:after {
|
||||
border-bottom-color: var(--white);
|
||||
}
|
||||
|
||||
.flatpickr-calendar.arrowBottom:before {
|
||||
border-top-color: var(--grey-200);
|
||||
}
|
||||
|
||||
.flatpickr-calendar.arrowBottom:after {
|
||||
border-top-color: var(--white);
|
||||
}
|
||||
|
||||
.flatpickr-months .flatpickr-month,
|
||||
.flatpickr-months .flatpickr-prev-month,
|
||||
.flatpickr-months .flatpickr-next-month {
|
||||
color: var(--text);
|
||||
fill: var(--grey-900);
|
||||
}
|
||||
|
||||
.flatpickr-months .flatpickr-prev-month:hover,
|
||||
.flatpickr-months .flatpickr-next-month:hover {
|
||||
color: var(--grey-400);
|
||||
}
|
||||
|
||||
.flatpickr-months .flatpickr-prev-month:hover svg,
|
||||
.flatpickr-months .flatpickr-next-month:hover svg {
|
||||
fill: var(--primary);
|
||||
}
|
||||
|
||||
.numInputWrapper span {
|
||||
border: 1px solid var(--grey-200);
|
||||
}
|
||||
|
||||
.numInputWrapper span:hover {
|
||||
background: var(--grey-800);
|
||||
}
|
||||
|
||||
.numInputWrapper span:active {
|
||||
background: var(--grey-800);
|
||||
}
|
||||
|
||||
.numInputWrapper span.arrowUp:after {
|
||||
border-bottom: 4px solid var(--grey-200);
|
||||
}
|
||||
|
||||
.numInputWrapper span.arrowDown:after {
|
||||
border-top: 4px solid var(--grey-200);
|
||||
top: 40%;
|
||||
}
|
||||
|
||||
.numInputWrapper span svg path {
|
||||
fill: var(--grey-800);
|
||||
}
|
||||
|
||||
.numInputWrapper:hover {
|
||||
background: var(--grey-100);
|
||||
}
|
||||
|
||||
.flatpickr-current-month span.cur-month:hover {
|
||||
background: var(--grey-100);
|
||||
}
|
||||
|
||||
.flatpickr-current-month .numInputWrapper span.arrowUp:after {
|
||||
border-bottom-color: var(--grey-900);
|
||||
}
|
||||
|
||||
.flatpickr-current-month .numInputWrapper span.arrowDown:after {
|
||||
border-top-color: var(--grey-900);
|
||||
}
|
||||
|
||||
.flatpickr-current-month input.cur-year[disabled],
|
||||
.flatpickr-current-month input.cur-year[disabled]:hover {
|
||||
color: var(--grey-800);
|
||||
}
|
||||
|
||||
.flatpickr-current-month .flatpickr-monthDropdown-months:hover {
|
||||
background: var(--grey-100);
|
||||
}
|
||||
|
||||
span.flatpickr-weekday {
|
||||
color: var(--grey-600);
|
||||
}
|
||||
|
||||
.dayContainer + .dayContainer {
|
||||
box-shadow: -1px 0 0 var(--grey-200);
|
||||
}
|
||||
|
||||
.flatpickr-day {
|
||||
color: var(--grey-500);
|
||||
}
|
||||
|
||||
.flatpickr-day.inRange,
|
||||
.flatpickr-day.prevMonthDay.inRange,
|
||||
.flatpickr-day.nextMonthDay.inRange,
|
||||
.flatpickr-day.today.inRange,
|
||||
.flatpickr-day.prevMonthDay.today.inRange,
|
||||
.flatpickr-day.nextMonthDay.today.inRange,
|
||||
.flatpickr-day:hover,
|
||||
.flatpickr-day.prevMonthDay:hover,
|
||||
.flatpickr-day.nextMonthDay:hover,
|
||||
.flatpickr-day:focus,
|
||||
.flatpickr-day.prevMonthDay:focus,
|
||||
.flatpickr-day.nextMonthDay:focus {
|
||||
background: var(--grey-200);
|
||||
border-color: var(--grey-200);
|
||||
}
|
||||
|
||||
.flatpickr-day.today {
|
||||
border-color: var(--grey-400);
|
||||
}
|
||||
|
||||
.flatpickr-day.today:hover,
|
||||
.flatpickr-day.today:focus {
|
||||
border-color: var(--grey-400);
|
||||
background: var(--grey-400);
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
.flatpickr-day.selected,
|
||||
.flatpickr-day.startRange,
|
||||
.flatpickr-day.endRange,
|
||||
.flatpickr-day.selected.inRange,
|
||||
.flatpickr-day.startRange.inRange,
|
||||
.flatpickr-day.endRange.inRange,
|
||||
.flatpickr-day.selected:focus,
|
||||
.flatpickr-day.startRange:focus,
|
||||
.flatpickr-day.endRange:focus,
|
||||
.flatpickr-day.selected:hover,
|
||||
.flatpickr-day.startRange:hover,
|
||||
.flatpickr-day.endRange:hover,
|
||||
.flatpickr-day.selected.prevMonthDay,
|
||||
.flatpickr-day.startRange.prevMonthDay,
|
||||
.flatpickr-day.endRange.prevMonthDay,
|
||||
.flatpickr-day.selected.nextMonthDay,
|
||||
.flatpickr-day.startRange.nextMonthDay,
|
||||
.flatpickr-day.endRange.nextMonthDay {
|
||||
background: var(--primary);
|
||||
color: var(--white);
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
.flatpickr-day.selected.startRange + .endRange:not(:nth-child(7n+1)),
|
||||
.flatpickr-day.startRange.startRange + .endRange:not(:nth-child(7n+1)),
|
||||
.flatpickr-day.endRange.startRange + .endRange:not(:nth-child(7n+1)) {
|
||||
box-shadow: -10px 0 0 var(--primary);
|
||||
}
|
||||
|
||||
.flatpickr-day.inRange {
|
||||
box-shadow: -5px 0 0 var(--grey-200), 5px 0 0 var(--grey-200);
|
||||
}
|
||||
|
||||
.flatpickr-day.flatpickr-disabled,
|
||||
.flatpickr-day.flatpickr-disabled:hover,
|
||||
.flatpickr-day.prevMonthDay,
|
||||
.flatpickr-day.nextMonthDay,
|
||||
.flatpickr-day.notAllowed,
|
||||
.flatpickr-day.notAllowed.prevMonthDay,
|
||||
.flatpickr-day.notAllowed.nextMonthDay {
|
||||
color: var(--grey-300);
|
||||
}
|
||||
|
||||
.flatpickr-day.flatpickr-disabled,
|
||||
.flatpickr-day.flatpickr-disabled:hover {
|
||||
color: var(--grey-800);
|
||||
}
|
||||
|
||||
.flatpickr-day.week.selected {
|
||||
box-shadow: -5px 0 0 var(--primary), 5px 0 0 var(--primary);
|
||||
}
|
||||
|
||||
.flatpickr-weekwrapper .flatpickr-weeks {
|
||||
box-shadow: 1px 0 0 var(--grey-200);
|
||||
}
|
||||
|
||||
.flatpickr-weekwrapper span.flatpickr-day,
|
||||
.flatpickr-weekwrapper span.flatpickr-day:hover {
|
||||
color: var(--grey-300);
|
||||
}
|
||||
|
||||
.flatpickr-time .numInputWrapper span.arrowUp:after {
|
||||
border-bottom-color: var(--grey-500);
|
||||
}
|
||||
|
||||
.flatpickr-time .numInputWrapper span.arrowDown:after {
|
||||
border-top-color: var(--grey-500);
|
||||
}
|
||||
|
||||
.flatpickr-time input {
|
||||
color: var(--grey-500);
|
||||
}
|
||||
|
||||
.flatpickr-time .flatpickr-time-separator,
|
||||
.flatpickr-time .flatpickr-am-pm {
|
||||
color: var(--grey-500);
|
||||
}
|
||||
|
||||
.flatpickr-time input:hover,
|
||||
.flatpickr-time .flatpickr-am-pm:hover,
|
||||
.flatpickr-time input:focus,
|
||||
.flatpickr-time .flatpickr-am-pm:focus {
|
||||
background: var(--grey-100);
|
||||
}
|
|
@ -40,6 +40,7 @@
|
|||
}
|
||||
|
||||
.select select {
|
||||
$thickness: 1px;
|
||||
border-width: $thickness;
|
||||
|
||||
&:not([multiple]) {
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
.notification {
|
||||
border: $thickness solid $border;
|
||||
}
|
||||
|
||||
.notifications {
|
||||
left: 0.5rem !important;
|
||||
bottom: 1rem !important;
|
||||
}
|
||||
|
||||
.message .message-body {
|
||||
border: $thickness solid;
|
||||
}
|
|
@ -3,7 +3,7 @@
|
|||
<h2 v-if="userInfo">
|
||||
{{ $t(`home.welcome${welcome}`, {username: userInfo.name !== '' ? userInfo.name : userInfo.username}) }}!
|
||||
</h2>
|
||||
<div class="notification is-danger" v-if="deletionScheduledAt !== null">
|
||||
<message variant="danger" v-if="deletionScheduledAt !== null">
|
||||
{{
|
||||
$t('user.deletion.scheduled', {
|
||||
date: formatDateShort(deletionScheduledAt),
|
||||
|
@ -13,7 +13,7 @@
|
|||
<router-link :to="{name: 'user.settings', hash: '#deletion'}">
|
||||
{{ $t('user.deletion.scheduledCancel') }}
|
||||
</router-link>
|
||||
</div>
|
||||
</message>
|
||||
<add-task
|
||||
:listId="defaultListId"
|
||||
@taskAdded="updateTaskList"
|
||||
|
@ -55,90 +55,70 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapState} from 'vuex'
|
||||
import ShowTasks from './tasks/ShowTasks.vue'
|
||||
import {getHistory} from '../modules/listHistory'
|
||||
<script lang="ts" setup>
|
||||
import {ref, computed} from 'vue'
|
||||
import {useStore} from 'vuex'
|
||||
import {useNow} from '@vueuse/core'
|
||||
|
||||
import Message from '@/components/misc/message.vue'
|
||||
import ShowTasks from '@/views/tasks/ShowTasks.vue'
|
||||
import ListCard from '@/components/list/partials/list-card.vue'
|
||||
import AddTask from '../components/tasks/add-task.vue'
|
||||
import {LOADING, LOADING_MODULE} from '../store/mutation-types'
|
||||
import {parseDateOrNull} from '../helpers/parseDateOrNull'
|
||||
import AddTask from '@/components/tasks/add-task.vue'
|
||||
|
||||
export default {
|
||||
name: 'Home',
|
||||
components: {
|
||||
ListCard,
|
||||
ShowTasks,
|
||||
AddTask,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
currentDate: new Date(),
|
||||
tasks: [],
|
||||
showTasksKey: 0,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
welcome() {
|
||||
const now = new Date()
|
||||
import {getHistory} from '@/modules/listHistory'
|
||||
import {parseDateOrNull} from '@/helpers/parseDateOrNull'
|
||||
import {formatDateShort, formatDateSince} from '@/helpers/time/formatDate'
|
||||
|
||||
if (now.getHours() < 5) {
|
||||
return 'Night'
|
||||
}
|
||||
const now = useNow()
|
||||
const welcome = computed(() => {
|
||||
const hours = new Date(now.value).getHours()
|
||||
|
||||
if (now.getHours() < 11) {
|
||||
return 'Morning'
|
||||
}
|
||||
if (hours < 5) {
|
||||
return 'Night'
|
||||
}
|
||||
|
||||
if (now.getHours() < 18) {
|
||||
return 'Day'
|
||||
}
|
||||
if (hours < 11) {
|
||||
return 'Morning'
|
||||
}
|
||||
|
||||
if (now.getHours() < 23) {
|
||||
return 'Evening'
|
||||
}
|
||||
if (hours < 18) {
|
||||
return 'Day'
|
||||
}
|
||||
|
||||
return 'Night'
|
||||
},
|
||||
listHistory() {
|
||||
const history = getHistory()
|
||||
return history.map(l => {
|
||||
return this.$store.getters['lists/getListById'](l.id)
|
||||
}).filter(l => l !== null)
|
||||
},
|
||||
...mapState({
|
||||
migratorsEnabled: state =>
|
||||
state.config.availableMigrators !== null &&
|
||||
state.config.availableMigrators.length > 0,
|
||||
authenticated: state => state.auth.authenticated,
|
||||
userInfo: state => state.auth.info,
|
||||
hasTasks: state => state.hasTasks,
|
||||
defaultListId: state => state.auth.defaultListId,
|
||||
defaultNamespaceId: state => {
|
||||
if (state.namespaces.namespaces.length === 0) {
|
||||
return 0
|
||||
}
|
||||
if (hours < 23) {
|
||||
return 'Evening'
|
||||
}
|
||||
|
||||
return state.namespaces.namespaces[0].id
|
||||
},
|
||||
hasLists: state => {
|
||||
if (state.namespaces.namespaces.length === 0) {
|
||||
return false
|
||||
}
|
||||
return 'Night'
|
||||
})
|
||||
|
||||
return state.namespaces.namespaces[0].lists.length > 0
|
||||
},
|
||||
loading: state => state[LOADING] && state[LOADING_MODULE] === 'tasks',
|
||||
deletionScheduledAt: state => parseDateOrNull(state.auth.info?.deletionScheduledAt),
|
||||
}),
|
||||
},
|
||||
methods: {
|
||||
// This is to reload the tasks list after adding a new task through the global task add.
|
||||
// FIXME: Should use vuex (somehow?)
|
||||
updateTaskList() {
|
||||
this.showTasksKey++
|
||||
},
|
||||
},
|
||||
const store = useStore()
|
||||
const listHistory = computed(() => {
|
||||
const history = getHistory()
|
||||
return history.map(l => {
|
||||
return store.getters['lists/getListById'](l.id)
|
||||
}).filter(l => l !== null)
|
||||
})
|
||||
|
||||
|
||||
const migratorsEnabled = computed(() => store.state.config.availableMigrators?.length > 0)
|
||||
const userInfo = computed(() => store.state.auth.info)
|
||||
const hasTasks = computed(() => store.state.hasTasks)
|
||||
const defaultListId = computed(() => store.state.auth.defaultListId)
|
||||
const defaultNamespaceId = computed(() => store.state.namespaces.namespaces?.[0].id || 0)
|
||||
const hasLists = computed (() => {
|
||||
return store.state.namespaces.namespaces.length === 0
|
||||
? false
|
||||
: store.state.namespaces.namespaces[0].lists.length > 0
|
||||
})
|
||||
const loading = computed(() => store.state.loading && store.state.loadingModule === 'tasks')
|
||||
const deletionScheduledAt = computed(() => parseDateOrNull(store.state.auth.info?.deletionScheduledAt))
|
||||
|
||||
// This is to reload the tasks list after adding a new task through the global task add.
|
||||
// FIXME: Should use vuex (somehow?)
|
||||
const showTasksKey = ref(0)
|
||||
function updateTaskList() {
|
||||
showTasksKey.value++
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -147,7 +127,7 @@ export default {
|
|||
flex-wrap: wrap;
|
||||
max-height: calc(#{$list-height * 2} + #{$list-spacing * 2} - 4px);
|
||||
overflow: hidden;
|
||||
|
||||
|
||||
@media screen and (max-width: $mobile) {
|
||||
max-height: calc(#{$list-height * 4} + #{$list-spacing * 4} - 4px);
|
||||
}
|
||||
|
|
|
@ -36,9 +36,9 @@
|
|||
</div>
|
||||
</div>
|
||||
<transition name="fade">
|
||||
<div class="notification is-warning" v-if="currentList.isArchived">
|
||||
<message variant="warning" v-if="currentList.isArchived" class="mb-4">
|
||||
{{ $t('list.archived') }}
|
||||
</div>
|
||||
</message>
|
||||
</transition>
|
||||
|
||||
<router-view/>
|
||||
|
@ -46,6 +46,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import Message from '@/components/misc/message'
|
||||
import ListModel from '../../models/list'
|
||||
import ListService from '../../services/list'
|
||||
import {CURRENT_LIST} from '../../store/mutation-types'
|
||||
|
@ -53,6 +54,7 @@ import {getListView} from '../../helpers/saveListView'
|
|||
import {saveListToHistory} from '../../modules/listHistory'
|
||||
|
||||
export default {
|
||||
components: {Message},
|
||||
data() {
|
||||
return {
|
||||
listService: new ListService(),
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
<div class="migration-in-progress">
|
||||
<img :alt="migrator.name" :src="migrator.icon" class="logo"/>
|
||||
<div class="progress-dots">
|
||||
<span v-for="i in progressDotsCount" :key="i" />
|
||||
<span v-for="i in progressDotsCount" :key="i"/>
|
||||
</div>
|
||||
<Logo class="logo"/>
|
||||
</div>
|
||||
|
@ -57,11 +57,9 @@
|
|||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="message is-primary">
|
||||
<div class="message-body">
|
||||
{{ message }}
|
||||
</div>
|
||||
</div>
|
||||
<message class="mb-4">
|
||||
{{ message }}
|
||||
</message>
|
||||
<x-button :to="{name: 'home'}">{{ $t('misc.refresh') }}</x-button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -71,6 +69,7 @@
|
|||
import AbstractMigrationService from '@/services/migrator/abstractMigration'
|
||||
import AbstractMigrationFileService from '@/services/migrator/abstractMigrationFile'
|
||||
import Logo from '@/assets/logo.svg?component'
|
||||
import Message from '@/components/misc/message'
|
||||
|
||||
import {MIGRATORS} from './migrators'
|
||||
|
||||
|
@ -79,7 +78,10 @@ const PROGRESS_DOTS_COUNT = 8
|
|||
export default {
|
||||
name: 'MigrateService',
|
||||
|
||||
components: { Logo },
|
||||
components: {
|
||||
Logo,
|
||||
Message,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
|
@ -101,7 +103,7 @@ export default {
|
|||
|
||||
beforeRouteEnter(to) {
|
||||
if (MIGRATORS[to.params.service] === undefined) {
|
||||
return { name: 'not-found' }
|
||||
return {name: 'not-found'}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -122,7 +124,7 @@ export default {
|
|||
if (this.migrator.isFileMigrator) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
this.authUrl = await this.migrationService.getAuthUrl().then(({url}) => url)
|
||||
|
||||
this.migratorAuthCode = location.hash.startsWith('#token=')
|
||||
|
@ -150,7 +152,7 @@ export default {
|
|||
this.lastMigrationDate = null
|
||||
this.message = ''
|
||||
|
||||
let migrationConfig = { code: this.migratorAuthCode }
|
||||
let migrationConfig = {code: this.migratorAuthCode}
|
||||
|
||||
if (this.migrator.isFileMigrator) {
|
||||
if (this.$refs.uploadInput.files.length === 0) {
|
||||
|
@ -160,7 +162,7 @@ export default {
|
|||
}
|
||||
|
||||
try {
|
||||
const { message } = await this.migrationService.migrate(migrationConfig)
|
||||
const {message} = await this.migrationService.migrate(migrationConfig)
|
||||
this.message = message
|
||||
return this.$store.dispatch('namespaces/loadNamespaces')
|
||||
} finally {
|
||||
|
@ -173,24 +175,24 @@ export default {
|
|||
|
||||
<style lang="scss" scoped>
|
||||
.migration-in-progress-container {
|
||||
max-width: 400px;
|
||||
margin: 4rem auto 0;
|
||||
text-align: center;
|
||||
max-width: 400px;
|
||||
margin: 4rem auto 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.migration-in-progress {
|
||||
text-align: center;
|
||||
display: flex;
|
||||
max-width: 400px;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 2rem;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
max-width: 400px;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.logo {
|
||||
display: block;
|
||||
max-height: 100px;
|
||||
max-width: 100px;
|
||||
display: block;
|
||||
max-height: 100px;
|
||||
max-width: 100px;
|
||||
}
|
||||
|
||||
.progress-dots {
|
||||
|
|
|
@ -28,19 +28,20 @@
|
|||
<div class="field">
|
||||
<label class="label">{{ $t('namespace.attributes.color') }}</label>
|
||||
<div class="control">
|
||||
<color-picker v-model="namespace.hexColor" />
|
||||
<color-picker v-model="namespace.hexColor"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="notification is-info mt-4">
|
||||
<message class="mt-4">
|
||||
<h4 class="title">{{ $t('namespace.create.tooltip') }}</h4>
|
||||
|
||||
{{ $t('namespace.create.explanation') }}
|
||||
</div>
|
||||
</message>
|
||||
</create-edit>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Message from '@/components/misc/message'
|
||||
import NamespaceModel from '../../models/namespace'
|
||||
import NamespaceService from '../../services/namespace'
|
||||
import CreateEdit from '@/components/misc/create-edit.vue'
|
||||
|
@ -56,6 +57,7 @@ export default {
|
|||
}
|
||||
},
|
||||
components: {
|
||||
Message,
|
||||
ColorPicker,
|
||||
CreateEdit,
|
||||
},
|
||||
|
@ -72,7 +74,7 @@ export default {
|
|||
|
||||
const namespace = await this.namespaceService.create(this.namespace)
|
||||
this.$store.commit('namespaces/addNamespace', namespace)
|
||||
this.$message.success({message: this.$t('namespace.create.success') })
|
||||
this.$message.success({message: this.$t('namespace.create.success')})
|
||||
this.$router.back()
|
||||
},
|
||||
},
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="notification is-info is-light has-text-centered" v-if="loading">
|
||||
<message v-if="loading">
|
||||
{{ $t('sharing.authenticating') }}
|
||||
</div>
|
||||
</message>
|
||||
<div v-if="authenticateWithPassword" class="box">
|
||||
<p class="pb-2">
|
||||
{{ $t('sharing.passwordRequired') }}
|
||||
|
@ -25,18 +25,20 @@
|
|||
{{ $t('user.auth.login') }}
|
||||
</x-button>
|
||||
|
||||
<div class="notification is-danger mt-4" v-if="errorMessage !== ''">
|
||||
<message variant="danger" class="mt-4" v-if="errorMessage !== ''">
|
||||
{{ errorMessage }}
|
||||
</div>
|
||||
</message>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapGetters} from 'vuex'
|
||||
import Message from '@/components/misc/message'
|
||||
|
||||
export default {
|
||||
name: 'LinkSharingAuth',
|
||||
components: {Message},
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
|
@ -72,7 +74,7 @@ export default {
|
|||
password: this.password,
|
||||
})
|
||||
this.$router.push({name: 'list.list', params: {listId: r.list_id}})
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
if (typeof e.response.data.code !== 'undefined' && e.response.data.code === 13001) {
|
||||
this.authenticateWithPassword = true
|
||||
return
|
||||
|
|
|
@ -486,7 +486,6 @@ export default {
|
|||
taskColor: '',
|
||||
|
||||
showDeleteModal: false,
|
||||
descriptionChanged: false,
|
||||
// Used to avoid flashing of empty elements if the task content is not yet loaded.
|
||||
visible: false,
|
||||
|
||||
|
@ -679,13 +678,6 @@ export default {
|
|||
this.saveTask(true, this.toggleTaskDone)
|
||||
},
|
||||
|
||||
setDescriptionChanged(e) {
|
||||
if (e.key === 'Enter' || e.key === 'Control') {
|
||||
return
|
||||
}
|
||||
this.descriptionChanged = true
|
||||
},
|
||||
|
||||
async changeList(list) {
|
||||
this.$store.commit('kanban/removeTaskInBucket', this.task)
|
||||
this.task.listId = list.id
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
<div>
|
||||
<h2 class="title has-text-centered">Login</h2>
|
||||
<div class="box">
|
||||
<div class="notification is-success has-text-centered" v-if="confirmedEmailSuccess">
|
||||
<message variant="success" class="has-text-centered" v-if="confirmedEmailSuccess">
|
||||
{{ $t('user.auth.confirmEmailSuccess') }}
|
||||
</div>
|
||||
</message>
|
||||
<api-config @foundApi="hasApiUrl = true"/>
|
||||
<form @submit.prevent="submit" id="loginform" v-if="hasApiUrl && localAuthEnabled">
|
||||
<div class="field">
|
||||
|
@ -78,9 +78,9 @@
|
|||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
<div class="notification is-danger" v-if="errorMessage">
|
||||
<message variant="danger" v-if="errorMessage">
|
||||
{{ errorMessage }}
|
||||
</div>
|
||||
</message>
|
||||
</form>
|
||||
|
||||
<div
|
||||
|
@ -110,11 +110,13 @@ import {LOADING} from '@/store/mutation-types'
|
|||
import legal from '../../components/misc/legal'
|
||||
import ApiConfig from '@/components/misc/api-config.vue'
|
||||
import {getErrorText} from '@/message'
|
||||
import Message from '@/components/misc/message'
|
||||
import {redirectToProvider} from '../../helpers/redirectToProvider'
|
||||
import {getLastVisited, clearLastVisited} from '../../helpers/saveLastVisited'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Message,
|
||||
ApiConfig,
|
||||
legal,
|
||||
},
|
||||
|
@ -149,7 +151,7 @@ export default {
|
|||
const last = getLastVisited()
|
||||
if (last !== null) {
|
||||
this.$router.push({
|
||||
name: last.name,
|
||||
name: last.name,
|
||||
params: last.params,
|
||||
})
|
||||
clearLastVisited()
|
||||
|
@ -206,7 +208,7 @@ export default {
|
|||
try {
|
||||
await this.$store.dispatch('auth/login', credentials)
|
||||
this.$store.commit('auth/needsTotpPasscode', false)
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
if (e.response && e.response.data.code === 1017 && !credentials.totpPasscode) {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="notification is-danger" v-if="errorMessage">
|
||||
<message variant="danger" v-if="errorMessage">
|
||||
{{ errorMessage }}
|
||||
</div>
|
||||
<div class="notification is-info" v-if="loading">
|
||||
</message>
|
||||
<message v-if="loading">
|
||||
{{ $t('user.auth.authenticating') }}
|
||||
</div>
|
||||
</message>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -14,10 +14,12 @@ import {mapState} from 'vuex'
|
|||
|
||||
import {LOADING} from '@/store/mutation-types'
|
||||
import {getErrorText} from '@/message'
|
||||
import Message from '@/components/misc/message'
|
||||
import {clearLastVisited, getLastVisited} from '../../helpers/saveLastVisited'
|
||||
|
||||
export default {
|
||||
name: 'Auth',
|
||||
components: {Message},
|
||||
data() {
|
||||
return {
|
||||
errorMessage: '',
|
||||
|
|
|
@ -45,37 +45,37 @@
|
|||
</x-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="notification is-info" v-if="this.passwordResetService.loading">
|
||||
<message v-if="this.passwordResetService.loading">
|
||||
{{ $t('misc.loading') }}
|
||||
</div>
|
||||
<div class="notification is-danger" v-if="errorMsg">
|
||||
</message>
|
||||
<message v-if="errorMsg">
|
||||
{{ errorMsg }}
|
||||
</div>
|
||||
</message>
|
||||
</form>
|
||||
<div class="has-text-centered" v-if="successMessage">
|
||||
<div class="notification is-success">
|
||||
<message variant="success">
|
||||
{{ successMessage }}
|
||||
</div>
|
||||
</message>
|
||||
<x-button :to="{ name: 'user.login' }">
|
||||
{{ $t('user.auth.login') }}
|
||||
</x-button>
|
||||
</div>
|
||||
<Legal />
|
||||
<Legal/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref, reactive} from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
|
||||
import Legal from '@/components/misc/legal'
|
||||
|
||||
import PasswordResetModel from '@/models/passwordReset'
|
||||
import PasswordResetService from '@/services/passwordReset'
|
||||
import { useTitle } from '@/composables/useTitle'
|
||||
import {useTitle} from '@/composables/useTitle'
|
||||
import Message from '@/components/misc/message'
|
||||
|
||||
const { t } = useI18n()
|
||||
const {t} = useI18n()
|
||||
useTitle(() => t('user.auth.resetPassword'))
|
||||
|
||||
const credentials = reactive({
|
||||
|
@ -97,10 +97,10 @@ async function submit() {
|
|||
|
||||
const passwordReset = new PasswordResetModel({newPassword: credentials.password})
|
||||
try {
|
||||
const { message } = passwordResetService.resetPassword(passwordReset)
|
||||
const {message} = passwordResetService.resetPassword(passwordReset)
|
||||
successMessage.value = message
|
||||
localStorage.removeItem('passwordResetToken')
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
errorMsg.value = e.response.data.message
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,12 +83,12 @@
|
|||
</x-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="notification is-info" v-if="loading">
|
||||
<message v-if="loading">
|
||||
{{ $t('misc.loading') }}
|
||||
</div>
|
||||
<div class="notification is-danger" v-if="errorMessage !== ''">
|
||||
</message>
|
||||
<message variant="danger" v-if="errorMessage !== ''">
|
||||
{{ errorMessage }}
|
||||
</div>
|
||||
</message>
|
||||
</form>
|
||||
<legal/>
|
||||
</div>
|
||||
|
@ -97,13 +97,13 @@
|
|||
|
||||
<script setup>
|
||||
import {ref, reactive, toRaw, computed, onBeforeMount} from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
|
||||
import router from '@/router'
|
||||
import { store } from '@/store'
|
||||
import { useTitle } from '@/composables/useTitle'
|
||||
|
||||
import {store} from '@/store'
|
||||
import {useTitle} from '@/composables/useTitle'
|
||||
import Legal from '@/components/misc/legal'
|
||||
import Message from '@/components/misc/message'
|
||||
|
||||
// FIXME: use the `beforeEnter` hook of vue-router
|
||||
// Check if the user is already logged in, if so, redirect them to the homepage
|
||||
|
@ -113,7 +113,7 @@ onBeforeMount(() => {
|
|||
}
|
||||
})
|
||||
|
||||
const { t } = useI18n()
|
||||
const {t} = useI18n()
|
||||
useTitle(() => t('user.auth.register'))
|
||||
|
||||
const credentials = reactive({
|
||||
|
@ -137,7 +137,7 @@ async function submit() {
|
|||
|
||||
try {
|
||||
await store.dispatch('auth/register', toRaw(credentials))
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
errorMessage.value = e.message
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,14 +31,14 @@
|
|||
</x-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="notification is-danger" v-if="errorMsg">
|
||||
<message variant="danger" v-if="errorMsg">
|
||||
{{ errorMsg }}
|
||||
</div>
|
||||
</message>
|
||||
</form>
|
||||
<div class="has-text-centered" v-if="isSuccess">
|
||||
<div class="notification is-success">
|
||||
<message variant="success">
|
||||
{{ $t('user.auth.resetPasswordSuccess') }}
|
||||
</div>
|
||||
</message>
|
||||
<x-button :to="{ name: 'user.login' }">
|
||||
{{ $t('user.auth.login') }}
|
||||
</x-button>
|
||||
|
@ -57,6 +57,7 @@ import Legal from '@/components/misc/legal'
|
|||
import PasswordResetModel from '@/models/passwordReset'
|
||||
import PasswordResetService from '@/services/passwordReset'
|
||||
import { useTitle } from '@/composables/useTitle'
|
||||
import Message from '@/components/misc/message'
|
||||
|
||||
const { t } = useI18n()
|
||||
useTitle(() => t('user.auth.resetPassword'))
|
||||
|
|
Loading…
Reference in New Issue