Merge remote-tracking branch 'upstream/main' into set-all-variables
continuous-integration/drone/pr Build is passing
Details
continuous-integration/drone/pr Build is passing
Details
This commit is contained in:
commit
3a85bf9b23
|
@ -202,7 +202,6 @@ steps:
|
||||||
YARN_CACHE_FOLDER: .cache/yarn/
|
YARN_CACHE_FOLDER: .cache/yarn/
|
||||||
commands:
|
commands:
|
||||||
- yarn --frozen-lockfile --network-timeout 100000
|
- yarn --frozen-lockfile --network-timeout 100000
|
||||||
- npx browserslist@latest --update-db
|
|
||||||
- yarn run lint
|
- yarn run lint
|
||||||
- "echo '{\"VERSION\": \"'$(git describe --tags --always --abbrev=10 | sed 's/-/+/' | sed 's/^v//' | sed 's/-g/-/')'\"}' > src/version.json"
|
- "echo '{\"VERSION\": \"'$(git describe --tags --always --abbrev=10 | sed 's/-/+/' | sed 's/^v//' | sed 's/-g/-/')'\"}' > src/version.json"
|
||||||
- yarn run build
|
- yarn run build
|
||||||
|
|
44
package.json
44
package.json
|
@ -19,10 +19,11 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@github/hotkey": "1.6.0",
|
"@github/hotkey": "1.6.0",
|
||||||
"@kyvg/vue3-notification": "2.3.4",
|
"@kyvg/vue3-notification": "2.3.4",
|
||||||
"@sentry/tracing": "6.16.0",
|
"@sentry/tracing": "6.16.1",
|
||||||
"@sentry/vue": "6.16.0",
|
"@sentry/vue": "6.16.1",
|
||||||
"@vue/compat": "3.2.24",
|
"@vue/compat": "3.2.26",
|
||||||
"@vueuse/core": "7.2.2",
|
"@vueuse/core": "7.3.0",
|
||||||
|
"@vueuse/router": "7.3.0",
|
||||||
"bulma-css-variables": "0.9.33",
|
"bulma-css-variables": "0.9.33",
|
||||||
"camel-case": "4.1.2",
|
"camel-case": "4.1.2",
|
||||||
"codemirror": "5.64.0",
|
"codemirror": "5.64.0",
|
||||||
|
@ -36,12 +37,12 @@
|
||||||
"is-touch-device": "1.0.1",
|
"is-touch-device": "1.0.1",
|
||||||
"lodash.clonedeep": "4.5.0",
|
"lodash.clonedeep": "4.5.0",
|
||||||
"lodash.debounce": "4.0.8",
|
"lodash.debounce": "4.0.8",
|
||||||
"marked": "4.0.6",
|
"marked": "4.0.7",
|
||||||
"register-service-worker": "1.7.2",
|
"register-service-worker": "1.7.2",
|
||||||
"snake-case": "3.0.4",
|
"snake-case": "3.0.4",
|
||||||
"ufo": "0.7.9",
|
"ufo": "0.7.9",
|
||||||
"v-tooltip": "4.0.0-beta.2",
|
"v-tooltip": "4.0.0-beta.2",
|
||||||
"vue": "3.2.24",
|
"vue": "3.2.26",
|
||||||
"vue-advanced-cropper": "2.7.0",
|
"vue-advanced-cropper": "2.7.0",
|
||||||
"vue-drag-resize": "2.0.3",
|
"vue-drag-resize": "2.0.3",
|
||||||
"vue-flatpickr-component": "9.0.5",
|
"vue-flatpickr-component": "9.0.5",
|
||||||
|
@ -59,34 +60,35 @@
|
||||||
"@fortawesome/vue-fontawesome": "3.0.0-5",
|
"@fortawesome/vue-fontawesome": "3.0.0-5",
|
||||||
"@types/flexsearch": "0.7.2",
|
"@types/flexsearch": "0.7.2",
|
||||||
"@types/jest": "27.0.3",
|
"@types/jest": "27.0.3",
|
||||||
"@typescript-eslint/eslint-plugin": "5.6.0",
|
"@typescript-eslint/eslint-plugin": "5.7.0",
|
||||||
"@typescript-eslint/parser": "5.6.0",
|
"@typescript-eslint/parser": "5.7.0",
|
||||||
"@vitejs/plugin-legacy": "1.6.4",
|
"@vitejs/plugin-legacy": "1.6.4",
|
||||||
"@vitejs/plugin-vue": "1.10.2",
|
"@vitejs/plugin-vue": "2.0.0",
|
||||||
"@vue/eslint-config-typescript": "9.1.0",
|
"@vue/eslint-config-typescript": "9.1.0",
|
||||||
"autoprefixer": "10.4.0",
|
"autoprefixer": "10.4.0",
|
||||||
"axios": "0.24.0",
|
"axios": "0.24.0",
|
||||||
"browserslist": "4.18.1",
|
"browserslist": "4.19.0",
|
||||||
"cypress": "8.7.0",
|
"caniuse-lite": "1.0.30001286",
|
||||||
|
"cypress": "9.1.1",
|
||||||
"cypress-file-upload": "5.0.8",
|
"cypress-file-upload": "5.0.8",
|
||||||
"esbuild": "0.14.2",
|
"esbuild": "0.14.3",
|
||||||
"eslint": "8.4.1",
|
"eslint": "8.4.1",
|
||||||
"eslint-plugin-vue": "8.2.0",
|
"eslint-plugin-vue": "8.2.0",
|
||||||
"express": "4.17.1",
|
"express": "4.17.1",
|
||||||
"faker": "5.5.3",
|
"faker": "5.5.3",
|
||||||
"jest": "27.4.3",
|
"jest": "27.4.5",
|
||||||
"netlify-cli": "8.0.16",
|
"netlify-cli": "8.1.1",
|
||||||
"postcss": "8.4.4",
|
"postcss": "8.4.5",
|
||||||
"postcss-preset-env": "7.0.1",
|
"postcss-preset-env": "7.0.1",
|
||||||
"rollup": "2.60.2",
|
"rollup": "2.61.1",
|
||||||
"rollup-plugin-visualizer": "5.5.2",
|
"rollup-plugin-visualizer": "5.5.2",
|
||||||
"sass": "1.44.0",
|
"sass": "1.45.0",
|
||||||
"slugify": "1.6.3",
|
"slugify": "1.6.3",
|
||||||
"ts-jest": "27.1.1",
|
"ts-jest": "27.1.1",
|
||||||
"typescript": "4.5.2",
|
"typescript": "4.5.4",
|
||||||
"vite": "2.7.1",
|
"vite": "2.7.2",
|
||||||
"vite-plugin-pwa": "0.11.10",
|
"vite-plugin-pwa": "0.11.11",
|
||||||
"vite-svg-loader": "3.1.0",
|
"vite-svg-loader": "3.1.1",
|
||||||
"vue-tsc": "0.29.8",
|
"vue-tsc": "0.29.8",
|
||||||
"wait-on": "6.0.0",
|
"wait-on": "6.0.0",
|
||||||
"workbox-cli": "6.4.2"
|
"workbox-cli": "6.4.2"
|
||||||
|
|
178
src/App.vue
178
src/App.vue
|
@ -1,116 +1,96 @@
|
||||||
<template>
|
<template>
|
||||||
<ready>
|
<ready :class="{'is-touch': isTouch}">
|
||||||
<div :class="{'is-touch': isTouch}">
|
<div :class="{'is-hidden': !online}">
|
||||||
<div :class="{'is-hidden': !online}">
|
<template v-if="authUser">
|
||||||
<template v-if="authUser">
|
<top-navigation/>
|
||||||
<top-navigation/>
|
<content-auth/>
|
||||||
<content-auth/>
|
</template>
|
||||||
</template>
|
<content-link-share v-else-if="authLinkShare"/>
|
||||||
<content-link-share v-else-if="authLinkShare"/>
|
<content-no-auth v-else/>
|
||||||
<content-no-auth v-else/>
|
<notification/>
|
||||||
<notification/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<transition name="fade">
|
|
||||||
<keyboard-shortcuts v-if="keyboardShortcutsActive"/>
|
|
||||||
</transition>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<transition name="fade">
|
||||||
|
<keyboard-shortcuts v-if="keyboardShortcutsActive"/>
|
||||||
|
</transition>
|
||||||
</ready>
|
</ready>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts" setup>
|
||||||
import {defineComponent} from 'vue'
|
import {computed, watch, watchEffect, Ref} from 'vue'
|
||||||
import {mapState, mapGetters} from 'vuex'
|
import {useRouter} from 'vue-router'
|
||||||
|
import {useRouteQuery} from '@vueuse/router'
|
||||||
|
import {useStore} from 'vuex'
|
||||||
|
import {useI18n} from 'vue-i18n'
|
||||||
|
import {useOnline} from '@vueuse/core'
|
||||||
import isTouchDevice from 'is-touch-device'
|
import isTouchDevice from 'is-touch-device'
|
||||||
|
import {success} from '@/message'
|
||||||
|
|
||||||
|
import Notification from '@/components/misc/notification.vue'
|
||||||
|
import KeyboardShortcuts from './components/misc/keyboard-shortcuts/index.vue'
|
||||||
|
import TopNavigation from './components/home/topNavigation.vue'
|
||||||
|
import ContentAuth from './components/home/contentAuth.vue'
|
||||||
|
import ContentLinkShare from './components/home/contentLinkShare.vue'
|
||||||
|
import ContentNoAuth from './components/home/contentNoAuth.vue'
|
||||||
|
import Ready from '@/components/misc/ready.vue'
|
||||||
|
|
||||||
import Notification from './components/misc/notification'
|
|
||||||
import {KEYBOARD_SHORTCUTS_ACTIVE, ONLINE} from './store/mutation-types'
|
|
||||||
import KeyboardShortcuts from './components/misc/keyboard-shortcuts'
|
|
||||||
import TopNavigation from './components/home/topNavigation'
|
|
||||||
import ContentAuth from './components/home/contentAuth'
|
|
||||||
import ContentLinkShare from './components/home/contentLinkShare'
|
|
||||||
import ContentNoAuth from './components/home/contentNoAuth'
|
|
||||||
import {setLanguage} from './i18n'
|
import {setLanguage} from './i18n'
|
||||||
import AccountDeleteService from '@/services/accountDelete'
|
import AccountDeleteService from '@/services/accountDelete'
|
||||||
import Ready from '@/components/misc/ready'
|
import {ONLINE} from '@/store/mutation-types'
|
||||||
|
|
||||||
import {useColorScheme} from '@/composables/useColorScheme'
|
import {useColorScheme} from '@/composables/useColorScheme'
|
||||||
|
|
||||||
export default defineComponent({
|
const store = useStore()
|
||||||
name: 'app',
|
const online = useOnline()
|
||||||
components: {
|
watchEffect(() => store.commit(ONLINE, online.value))
|
||||||
ContentNoAuth,
|
|
||||||
ContentLinkShare,
|
|
||||||
ContentAuth,
|
|
||||||
TopNavigation,
|
|
||||||
KeyboardShortcuts,
|
|
||||||
Notification,
|
|
||||||
Ready,
|
|
||||||
},
|
|
||||||
beforeMount() {
|
|
||||||
this.setupOnlineStatus()
|
|
||||||
this.setupPasswortResetRedirect()
|
|
||||||
this.setupEmailVerificationRedirect()
|
|
||||||
this.setupAccountDeletionVerification()
|
|
||||||
},
|
|
||||||
beforeCreate() {
|
|
||||||
setLanguage()
|
|
||||||
},
|
|
||||||
setup() {
|
|
||||||
useColorScheme()
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
// Make sure to always load the home route when running with electron
|
|
||||||
if (this.$route.fullPath.endsWith('frontend/index.html')) {
|
|
||||||
this.$router.push({name: 'home'})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
isTouch() {
|
|
||||||
return isTouchDevice()
|
|
||||||
},
|
|
||||||
...mapState({
|
|
||||||
online: ONLINE,
|
|
||||||
keyboardShortcutsActive: KEYBOARD_SHORTCUTS_ACTIVE,
|
|
||||||
}),
|
|
||||||
...mapGetters('auth', [
|
|
||||||
'authUser',
|
|
||||||
'authLinkShare',
|
|
||||||
]),
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
setupOnlineStatus() {
|
|
||||||
this.$store.commit(ONLINE, navigator.onLine)
|
|
||||||
window.addEventListener('online', () => this.$store.commit(ONLINE, navigator.onLine))
|
|
||||||
window.addEventListener('offline', () => this.$store.commit(ONLINE, navigator.onLine))
|
|
||||||
},
|
|
||||||
setupPasswortResetRedirect() {
|
|
||||||
if (typeof this.$route.query.userPasswordReset === 'undefined') {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
localStorage.setItem('passwordResetToken', this.$route.query.userPasswordReset)
|
const router = useRouter()
|
||||||
this.$router.push({name: 'user.password-reset.reset'})
|
|
||||||
},
|
|
||||||
setupEmailVerificationRedirect() {
|
|
||||||
if (typeof this.$route.query.userEmailConfirm === 'undefined') {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
localStorage.setItem('emailConfirmToken', this.$route.query.userEmailConfirm)
|
const isTouch = computed(isTouchDevice)
|
||||||
this.$router.push({name: 'user.login'})
|
const keyboardShortcutsActive = computed(() => store.state.keyboardShortcutsActive)
|
||||||
},
|
|
||||||
async setupAccountDeletionVerification() {
|
|
||||||
if (typeof this.$route.query.accountDeletionConfirm === 'undefined') {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const accountDeletionService = new AccountDeleteService()
|
const authUser = computed(() => store.getters['auth/authUser'])
|
||||||
await accountDeletionService.confirm(this.$route.query.accountDeletionConfirm)
|
const authLinkShare = computed(() => store.getters['auth/authLinkShare'])
|
||||||
this.$message.success({message: this.$t('user.deletion.confirmSuccess')})
|
|
||||||
this.$store.dispatch('auth/refreshUserInfo')
|
const {t} = useI18n()
|
||||||
},
|
|
||||||
},
|
// setup account deletion verification
|
||||||
})
|
const accountDeletionConfirm = useRouteQuery('accountDeletionConfirm') as Ref<null | string>
|
||||||
|
watch(accountDeletionConfirm, async (accountDeletionConfirm) => {
|
||||||
|
if (accountDeletionConfirm === null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const accountDeletionService = new AccountDeleteService()
|
||||||
|
await accountDeletionService.confirm(accountDeletionConfirm)
|
||||||
|
success({message: t('user.deletion.confirmSuccess')})
|
||||||
|
store.dispatch('auth/refreshUserInfo')
|
||||||
|
}, { immediate: true })
|
||||||
|
|
||||||
|
// setup passwort reset redirect
|
||||||
|
const userPasswordReset = useRouteQuery('userPasswordReset') as Ref<null | string>
|
||||||
|
watch(userPasswordReset, (userPasswordReset) => {
|
||||||
|
if (userPasswordReset === null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
localStorage.setItem('passwordResetToken', userPasswordReset)
|
||||||
|
router.push({name: 'user.password-reset.reset'})
|
||||||
|
}, { immediate: true })
|
||||||
|
|
||||||
|
// setup email verification redirect
|
||||||
|
const userEmailConfirm = useRouteQuery('userEmailConfirm') as Ref<null | string>
|
||||||
|
watch(userEmailConfirm, (userEmailConfirm) => {
|
||||||
|
if (userEmailConfirm === null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
localStorage.setItem('emailConfirmToken', userEmailConfirm)
|
||||||
|
router.push({name: 'user.login'})
|
||||||
|
}, { immediate: true })
|
||||||
|
|
||||||
|
setLanguage()
|
||||||
|
useColorScheme()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 519 KiB |
|
@ -40,96 +40,88 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts" setup>
|
||||||
import {mapState} from 'vuex'
|
import {watch, computed} from 'vue'
|
||||||
|
import {useStore} from 'vuex'
|
||||||
|
import {useRoute, useRouter} from 'vue-router'
|
||||||
|
import {useEventListener} from '@vueuse/core'
|
||||||
|
|
||||||
import {CURRENT_LIST, KEYBOARD_SHORTCUTS_ACTIVE, MENU_ACTIVE} from '@/store/mutation-types'
|
import {CURRENT_LIST, KEYBOARD_SHORTCUTS_ACTIVE, MENU_ACTIVE} from '@/store/mutation-types'
|
||||||
import Navigation from '@/components/home/navigation.vue'
|
import Navigation from '@/components/home/navigation.vue'
|
||||||
import QuickActions from '@/components/quick-actions/quick-actions.vue'
|
import QuickActions from '@/components/quick-actions/quick-actions.vue'
|
||||||
|
|
||||||
export default {
|
const store = useStore()
|
||||||
name: 'contentAuth',
|
|
||||||
components: {QuickActions, Navigation},
|
|
||||||
watch: {
|
|
||||||
'$route': {
|
|
||||||
handler: 'doStuffAfterRoute',
|
|
||||||
deep: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
this.renewTokenOnFocus()
|
|
||||||
this.loadLabels()
|
|
||||||
},
|
|
||||||
computed: mapState({
|
|
||||||
background: 'background',
|
|
||||||
menuActive: MENU_ACTIVE,
|
|
||||||
userInfo: state => state.auth.info,
|
|
||||||
authenticated: state => state.auth.authenticated,
|
|
||||||
}),
|
|
||||||
methods: {
|
|
||||||
doStuffAfterRoute() {
|
|
||||||
// this.setTitle('') // Reset the title if the page component does not set one itself
|
|
||||||
this.hideMenuOnMobile()
|
|
||||||
this.resetCurrentList()
|
|
||||||
},
|
|
||||||
resetCurrentList() {
|
|
||||||
// Reset the current list highlight in menu if the current list is not list related.
|
|
||||||
if (
|
|
||||||
this.$route.name === 'home' ||
|
|
||||||
this.$route.name === 'namespace.edit' ||
|
|
||||||
this.$route.name === 'teams.index' ||
|
|
||||||
this.$route.name === 'teams.edit' ||
|
|
||||||
this.$route.name === 'tasks.range' ||
|
|
||||||
this.$route.name === 'labels.index' ||
|
|
||||||
this.$route.name === 'migrate.start' ||
|
|
||||||
this.$route.name === 'migrate.wunderlist' ||
|
|
||||||
this.$route.name.startsWith('user.settings') ||
|
|
||||||
this.$route.name === 'namespaces.index'
|
|
||||||
) {
|
|
||||||
return this.$store.dispatch(CURRENT_LIST, null)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
renewTokenOnFocus() {
|
|
||||||
// Try renewing the token every time vikunja is loaded initially
|
|
||||||
// (When opening the browser the focus event is not fired)
|
|
||||||
this.$store.dispatch('auth/renewToken')
|
|
||||||
|
|
||||||
// Check if the token is still valid if the window gets focus again to maybe renew it
|
const background = computed(() => store.state.background)
|
||||||
window.addEventListener('focus', () => {
|
const menuActive = computed(() => store.state.menuActive)
|
||||||
|
|
||||||
if (!this.authenticated) {
|
function showKeyboardShortcuts() {
|
||||||
return
|
store.commit(KEYBOARD_SHORTCUTS_ACTIVE, true)
|
||||||
}
|
|
||||||
|
|
||||||
const expiresIn = (this.userInfo !== null ? this.userInfo.exp : 0) - +new Date() / 1000
|
|
||||||
|
|
||||||
// If the token expiry is negative, it is already expired and we have no choice but to redirect
|
|
||||||
// the user to the login page
|
|
||||||
if (expiresIn < 0) {
|
|
||||||
this.$store.dispatch('auth/checkAuth')
|
|
||||||
this.$router.push({name: 'user.login'})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the token is valid for less than 60 hours and renew if thats the case
|
|
||||||
if (expiresIn < 60 * 3600) {
|
|
||||||
this.$store.dispatch('auth/renewToken')
|
|
||||||
console.debug('renewed token')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
hideMenuOnMobile() {
|
|
||||||
if (window.innerWidth < 769) {
|
|
||||||
this.$store.commit(MENU_ACTIVE, false)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
showKeyboardShortcuts() {
|
|
||||||
this.$store.commit(KEYBOARD_SHORTCUTS_ACTIVE, true)
|
|
||||||
},
|
|
||||||
loadLabels() {
|
|
||||||
this.$store.dispatch('labels/loadAllLabels')
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
// hide menu on mobile
|
||||||
|
watch(() => route.fullPath, () => window.innerWidth < 769 && store.commit(MENU_ACTIVE, false))
|
||||||
|
|
||||||
|
// Reset the current list highlight in menu if the current route is not list related.
|
||||||
|
watch(() => route.fullPath, () => {
|
||||||
|
if (
|
||||||
|
[
|
||||||
|
'home',
|
||||||
|
'namespace.edit',
|
||||||
|
'teams.index',
|
||||||
|
'teams.edit',
|
||||||
|
'tasks.range',
|
||||||
|
'labels.index',
|
||||||
|
'migrate.start',
|
||||||
|
'migrate.wunderlist',
|
||||||
|
'namespaces.index',
|
||||||
|
].includes(route.name) ||
|
||||||
|
route.name.startsWith('user.settings')
|
||||||
|
) {
|
||||||
|
store.dispatch(CURRENT_LIST, null)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// TODO: Reset the title if the page component does not set one itself
|
||||||
|
|
||||||
|
function useRenewTokenOnFocus() {
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const userInfo = computed(() => store.state.auth.info)
|
||||||
|
const authenticated = computed(() => store.state.auth.authenticated)
|
||||||
|
|
||||||
|
// Try renewing the token every time vikunja is loaded initially
|
||||||
|
// (When opening the browser the focus event is not fired)
|
||||||
|
store.dispatch('auth/renewToken')
|
||||||
|
|
||||||
|
// Check if the token is still valid if the window gets focus again to maybe renew it
|
||||||
|
useEventListener('focus', () => {
|
||||||
|
if (!authenticated.value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const expiresIn = (userInfo.value !== null ? userInfo.value.exp : 0) - +new Date() / 1000
|
||||||
|
|
||||||
|
// If the token expiry is negative, it is already expired and we have no choice but to redirect
|
||||||
|
// the user to the login page
|
||||||
|
if (expiresIn < 0) {
|
||||||
|
store.dispatch('auth/checkAuth')
|
||||||
|
router.push({name: 'user.login'})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the token is valid for less than 60 hours and renew if thats the case
|
||||||
|
if (expiresIn < 60 * 3600) {
|
||||||
|
store.dispatch('auth/renewToken')
|
||||||
|
console.debug('renewed token')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
useRenewTokenOnFocus()
|
||||||
|
store.dispatch('labels/loadAllLabels')
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
|
@ -21,23 +21,16 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts" setup>
|
||||||
import {mapState} from 'vuex'
|
import {computed} from 'vue'
|
||||||
|
import {useStore} from 'vuex'
|
||||||
|
|
||||||
import Logo from '@/components/home/Logo.vue'
|
import Logo from '@/components/home/Logo.vue'
|
||||||
import PoweredByLink from './PoweredByLink.vue'
|
import PoweredByLink from './PoweredByLink.vue'
|
||||||
|
|
||||||
export default {
|
const store = useStore()
|
||||||
name: 'contentLinkShare',
|
const currentList = computed(() => store.state.currentList)
|
||||||
components: {
|
const background = computed(() => store.state.background)
|
||||||
Logo,
|
|
||||||
PoweredByLink,
|
|
||||||
},
|
|
||||||
computed: mapState([
|
|
||||||
'currentList',
|
|
||||||
'background',
|
|
||||||
]),
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
|
@ -4,44 +4,38 @@
|
||||||
</no-auth-wrapper>
|
</no-auth-wrapper>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts" setup>
|
||||||
import {saveLastVisited} from '@/helpers/saveLastVisited'
|
import {watchEffect} from 'vue'
|
||||||
|
import {useRoute, useRouter} from 'vue-router'
|
||||||
|
|
||||||
import NoAuthWrapper from '@/components/misc/no-auth-wrapper'
|
import NoAuthWrapper from '@/components/misc/no-auth-wrapper'
|
||||||
|
|
||||||
export default {
|
import {saveLastVisited} from '@/helpers/saveLastVisited'
|
||||||
name: 'contentNoAuth',
|
|
||||||
components: {NoAuthWrapper},
|
const route = useRoute()
|
||||||
computed: {
|
|
||||||
routeName() {
|
watchEffect(() => {
|
||||||
return this.$route.name
|
if (!route.name) return
|
||||||
},
|
redirectToHome()
|
||||||
},
|
})
|
||||||
watch: {
|
|
||||||
routeName: {
|
const router = useRouter()
|
||||||
handler(routeName) {
|
function redirectToHome() {
|
||||||
if (!routeName) return
|
// Check if the user is already logged in and redirect them to the home page if not
|
||||||
this.redirectToHome()
|
if (
|
||||||
},
|
![
|
||||||
immediate: true,
|
'user.login',
|
||||||
},
|
'user.password-reset.request',
|
||||||
},
|
'user.password-reset.reset',
|
||||||
methods: {
|
'user.register',
|
||||||
redirectToHome() {
|
'link-share.auth',
|
||||||
// Check if the user is already logged in and redirect them to the home page if not
|
'openid.auth',
|
||||||
if (
|
].includes(route.name) &&
|
||||||
this.$route.name !== 'user.login' &&
|
localStorage.getItem('passwordResetToken') === null &&
|
||||||
this.$route.name !== 'user.password-reset.request' &&
|
localStorage.getItem('emailConfirmToken') === null
|
||||||
this.$route.name !== 'user.password-reset.reset' &&
|
) {
|
||||||
this.$route.name !== 'user.register' &&
|
saveLastVisited(route.name, route.params)
|
||||||
this.$route.name !== 'link-share.auth' &&
|
router.push({name: 'user.login'})
|
||||||
this.$route.name !== 'openid.auth' &&
|
}
|
||||||
localStorage.getItem('passwordResetToken') === null &&
|
|
||||||
localStorage.getItem('emailConfirmToken') === null
|
|
||||||
) {
|
|
||||||
saveLastVisited(this.$route.name, this.$route.params)
|
|
||||||
this.$router.push({name: 'user.login'})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -457,7 +457,7 @@ export default {
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
font-family: $family-sans-serif;
|
font-family: $family-sans-serif;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
padding: .5rem 0;
|
padding: .5rem;
|
||||||
border: none;
|
border: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
|
@ -477,7 +477,7 @@ export default {
|
||||||
font-size: .75rem;
|
font-size: .75rem;
|
||||||
color: transparent;
|
color: transparent;
|
||||||
transition: color $transition;
|
transition: color $transition;
|
||||||
padding: 0 .5rem;
|
padding-left: .5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:focus, &:hover {
|
&:focus, &:hover {
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="api-url-info" v-else>
|
<div class="api-url-info" v-else>
|
||||||
<i18n-t keypath="apiConfig.signInOn">
|
<i18n-t keypath="apiConfig.use">
|
||||||
<span class="url" v-tooltip="apiUrl"> {{ apiDomain }} </span>
|
<span class="url" v-tooltip="apiUrl"> {{ apiDomain }} </span>
|
||||||
</i18n-t>
|
</i18n-t>
|
||||||
<br/>
|
<br/>
|
||||||
|
@ -101,7 +101,7 @@ export default {
|
||||||
|
|
||||||
// Set it + save it to local storage to save us the hoops
|
// Set it + save it to local storage to save us the hoops
|
||||||
this.errorMsg = ''
|
this.errorMsg = ''
|
||||||
this.successMsg = this.$t('apiConfig.success', {domain: this.apiDomain})
|
this.$message.success({message: this.$t('apiConfig.success', {domain: this.apiDomain})})
|
||||||
this.configureApi = false
|
this.configureApi = false
|
||||||
this.apiUrl = url
|
this.apiUrl = url
|
||||||
this.$emit('foundApi', this.apiUrl)
|
this.$emit('foundApi', this.apiUrl)
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="message" :class="variant">
|
<div class="message-wrapper">
|
||||||
<slot/>
|
<div class="message" :class="variant">
|
||||||
|
<slot/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -14,6 +16,11 @@ defineProps({
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
.message-wrapper {
|
||||||
|
border-radius: $radius;
|
||||||
|
background: var(--white);
|
||||||
|
}
|
||||||
|
|
||||||
.message {
|
.message {
|
||||||
padding: .75rem 1rem;
|
padding: .75rem 1rem;
|
||||||
border-radius: $radius;
|
border-radius: $radius;
|
||||||
|
|
|
@ -1,40 +1,134 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="no-auth-wrapper">
|
<div class="no-auth-wrapper">
|
||||||
|
<Logo class="logo" width="200" height="58"/>
|
||||||
<div class="noauth-container">
|
<div class="noauth-container">
|
||||||
<Logo class="logo" width="400" height="117" />
|
<section class="image" :class="{'has-message': motd !== ''}">
|
||||||
<Message v-if="motd !== ''" class="my-2">
|
<Message v-if="motd !== ''">
|
||||||
{{ motd }}
|
{{ motd }}
|
||||||
</Message>
|
</Message>
|
||||||
<slot/>
|
<h2 class="image-title">
|
||||||
|
{{ $t('misc.welcomeBack') }}
|
||||||
|
</h2>
|
||||||
|
</section>
|
||||||
|
<section class="content">
|
||||||
|
<div>
|
||||||
|
<h2 class="title" v-if="title">{{ title }}</h2>
|
||||||
|
<api-config @foundApi="hasApiUrl = true"/>
|
||||||
|
<slot/>
|
||||||
|
</div>
|
||||||
|
<legal/>
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import Logo from '@/components/home/Logo.vue'
|
import Logo from '@/components/home/Logo'
|
||||||
import Message from '@/components/misc/message.vue'
|
import Message from '@/components/misc/message'
|
||||||
|
import Legal from '@/components/misc/legal'
|
||||||
|
import ApiConfig from '@/components/misc/api-config.vue'
|
||||||
import {useStore} from 'vuex'
|
import {useStore} from 'vuex'
|
||||||
import {computed} from 'vue'
|
import {computed} from 'vue'
|
||||||
|
import {useRoute} from 'vue-router'
|
||||||
|
import {useI18n} from 'vue-i18n'
|
||||||
|
import {useTitle} from '@/composables/useTitle'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
const store = useStore()
|
const store = useStore()
|
||||||
|
const {t} = useI18n()
|
||||||
|
|
||||||
const motd = computed(() => store.state.config.motd)
|
const motd = computed(() => store.state.config.motd)
|
||||||
|
// @ts-ignore
|
||||||
|
const title = computed(() => t(route.meta.title ?? ''))
|
||||||
|
useTitle(() => title.value)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.no-auth-wrapper {
|
.no-auth-wrapper {
|
||||||
background: url('@/assets/llama.svg?url') no-repeat bottom left fixed var(--site-background);
|
background: var(--site-background) url('@/assets/llama.svg?url') no-repeat fixed bottom left;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
place-items: center;
|
||||||
|
|
||||||
|
@media screen and (max-width: $fullhd) {
|
||||||
|
padding-bottom: 15rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.noauth-container {
|
.noauth-container {
|
||||||
max-width: 450px;
|
max-width: $desktop;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 0 auto;
|
min-height: 60vh;
|
||||||
|
display: flex;
|
||||||
|
background-color: var(--white);
|
||||||
|
box-shadow: var(--shadow-md);
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
@media screen and (min-width: $desktop) {
|
||||||
|
border-radius: $radius;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.image {
|
||||||
|
width: 50%;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-end;
|
||||||
|
|
||||||
|
@media screen and (max-width: $tablet) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: $tablet) {
|
||||||
|
background: url('@/assets/no-auth-image.jpg') no-repeat bottom/cover;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&.has-message {
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: rgba(0, 0, 0, .2);
|
||||||
|
}
|
||||||
|
|
||||||
|
> * {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 2rem 2rem 1.5rem;
|
||||||
|
|
||||||
|
@media screen and (max-width: $desktop) {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 450px;
|
||||||
|
margin-inline: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: $desktop) {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo {
|
.logo {
|
||||||
color: var(--logo-text-color);
|
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
|
margin: 1rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-title {
|
||||||
|
color: var(--white);
|
||||||
|
font-size: 2.5rem;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -21,11 +21,11 @@
|
||||||
<li :key="`page-${i}`" v-for="(p, i) in pages">
|
<li :key="`page-${i}`" v-for="(p, i) in pages">
|
||||||
<span class="pagination-ellipsis" v-if="p.isEllipsis">…</span>
|
<span class="pagination-ellipsis" v-if="p.isEllipsis">…</span>
|
||||||
<router-link
|
<router-link
|
||||||
|
v-else
|
||||||
|
class="pagination-link"
|
||||||
:aria-label="'Goto page ' + p.number"
|
:aria-label="'Goto page ' + p.number"
|
||||||
:class="{ 'is-current': p.number === currentPage }"
|
:class="{ 'is-current': p.number === currentPage }"
|
||||||
:to="getRouteForPagination(p.number)"
|
:to="getRouteForPagination(p.number)"
|
||||||
class="pagination-link"
|
|
||||||
v-else
|
|
||||||
>
|
>
|
||||||
{{ p.number }}
|
{{ p.number }}
|
||||||
</router-link>
|
</router-link>
|
||||||
|
@ -98,13 +98,13 @@ const pages = computed(() => createPagination(props.totalPages, props.currentPag
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.pagination {
|
.pagination {
|
||||||
padding-bottom: 1rem;
|
padding-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
.pagination-previous,
|
.pagination-previous,
|
||||||
.pagination-next {
|
.pagination-next {
|
||||||
&:not(:disabled):hover {
|
&:not(:disabled):hover {
|
||||||
background: $scheme-main;
|
background: $scheme-main;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
|
@ -16,60 +16,54 @@
|
||||||
</multiselect>
|
</multiselect>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts" setup>
|
||||||
import ListModel from '../../../models/list'
|
import {reactive, ref, watchEffect} from 'vue'
|
||||||
|
import {useStore} from 'vuex'
|
||||||
|
import {useI18n} from 'vue-i18n'
|
||||||
|
import ListModel from '@/models/list'
|
||||||
import Multiselect from '@/components/input/multiselect.vue'
|
import Multiselect from '@/components/input/multiselect.vue'
|
||||||
|
|
||||||
export default {
|
const store = useStore()
|
||||||
name: 'listSearch',
|
const {t} = useI18n()
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
list: new ListModel(),
|
|
||||||
foundLists: [],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
modelValue: {
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
emits: ['update:modelValue', 'selected'],
|
|
||||||
components: {
|
|
||||||
Multiselect,
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
modelValue: {
|
|
||||||
handler(value) {
|
|
||||||
this.list = value
|
|
||||||
},
|
|
||||||
immeditate: true,
|
|
||||||
deep: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
findLists(query) {
|
|
||||||
this.foundLists = this.$store.getters['lists/searchList'](query)
|
|
||||||
},
|
|
||||||
|
|
||||||
select(list) {
|
const list = reactive(new ListModel())
|
||||||
this.list = list
|
const props = defineProps({
|
||||||
this.$emit('selected', list)
|
modelValue: {
|
||||||
this.$emit('update:modelValue', list)
|
validator(value) {
|
||||||
},
|
return value instanceof ListModel
|
||||||
|
|
||||||
namespace(namespaceId) {
|
|
||||||
const namespace = this.$store.getters['namespaces/getNamespaceById'](namespaceId)
|
|
||||||
if (namespace !== null) {
|
|
||||||
return namespace.title
|
|
||||||
}
|
|
||||||
return this.$t('list.shared')
|
|
||||||
},
|
},
|
||||||
|
required: false,
|
||||||
},
|
},
|
||||||
|
})
|
||||||
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
Object.assign(list, props.modelValue)
|
||||||
|
})
|
||||||
|
|
||||||
|
const foundLists = ref([])
|
||||||
|
function findLists(query: string) {
|
||||||
|
if (query === '') {
|
||||||
|
select(null)
|
||||||
|
}
|
||||||
|
foundLists.value = store.getters['lists/searchList'](query)
|
||||||
|
}
|
||||||
|
|
||||||
|
function select(l: ListModel | null) {
|
||||||
|
Object.assign(list, l)
|
||||||
|
emit('update:modelValue', list)
|
||||||
|
}
|
||||||
|
|
||||||
|
function namespace(namespaceId: number) {
|
||||||
|
const namespace = store.getters['namespaces/getNamespaceById'](namespaceId)
|
||||||
|
return namespace !== null
|
||||||
|
? namespace.title
|
||||||
|
: t('list.shared')
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.list-namespace-title {
|
.list-namespace-title {
|
||||||
color: var(--grey-500);
|
color: var(--grey-500);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
|
@ -36,6 +36,7 @@
|
||||||
"password": "Heslo",
|
"password": "Heslo",
|
||||||
"passwordRepeat": "Zopakovat heslo",
|
"passwordRepeat": "Zopakovat heslo",
|
||||||
"passwordPlaceholder": "např. • • • • • • • •",
|
"passwordPlaceholder": "např. • • • • • • • •",
|
||||||
|
"forgotPassword": "Forgot your password?",
|
||||||
"resetPassword": "Obnovit heslo",
|
"resetPassword": "Obnovit heslo",
|
||||||
"resetPasswordAction": "Poslat odkaz na obnovení hesla",
|
"resetPasswordAction": "Poslat odkaz na obnovení hesla",
|
||||||
"resetPasswordSuccess": "Zkontrolujte doručenou poštu! Měli byste mít e-mail s pokyny, jak obnovit své heslo.",
|
"resetPasswordSuccess": "Zkontrolujte doručenou poštu! Měli byste mít e-mail s pokyny, jak obnovit své heslo.",
|
||||||
|
@ -473,7 +474,8 @@
|
||||||
"download": "Stáhnout",
|
"download": "Stáhnout",
|
||||||
"showMenu": "Zobrazit nabídku",
|
"showMenu": "Zobrazit nabídku",
|
||||||
"hideMenu": "Skrýt nabídku",
|
"hideMenu": "Skrýt nabídku",
|
||||||
"forExample": "Například:"
|
"forExample": "Například:",
|
||||||
|
"welcomeBack": "Welcome Back!"
|
||||||
},
|
},
|
||||||
"input": {
|
"input": {
|
||||||
"resetColor": "Obnovit barvu",
|
"resetColor": "Obnovit barvu",
|
||||||
|
@ -811,7 +813,7 @@
|
||||||
"url": "Vikunja URL",
|
"url": "Vikunja URL",
|
||||||
"urlPlaceholder": "např. https://localhost:3456",
|
"urlPlaceholder": "např. https://localhost:3456",
|
||||||
"change": "změnit",
|
"change": "změnit",
|
||||||
"signInOn": "Přihlaste se ke svému účtu Vikunja na {0}",
|
"use": "Using Vikunja installation at {0}",
|
||||||
"error": "Nelze najít nebo použít instalaci Vikunja na \"{domain}\". Zkuste prosím jinou url.",
|
"error": "Nelze najít nebo použít instalaci Vikunja na \"{domain}\". Zkuste prosím jinou url.",
|
||||||
"success": "Pomocí instalace Vikunja na \"{domain}\".",
|
"success": "Pomocí instalace Vikunja na \"{domain}\".",
|
||||||
"urlRequired": "Je vyžadována adresa URL."
|
"urlRequired": "Je vyžadována adresa URL."
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
"password": "Passwort",
|
"password": "Passwort",
|
||||||
"passwordRepeat": "Gib dein Passwort erneut ein",
|
"passwordRepeat": "Gib dein Passwort erneut ein",
|
||||||
"passwordPlaceholder": "z.B. •••••••••••",
|
"passwordPlaceholder": "z.B. •••••••••••",
|
||||||
|
"forgotPassword": "Forgot your password?",
|
||||||
"resetPassword": "Setze dein Passwort zurück",
|
"resetPassword": "Setze dein Passwort zurück",
|
||||||
"resetPasswordAction": "Sende mir einen Link zum Zurücksetzen des Passworts",
|
"resetPasswordAction": "Sende mir einen Link zum Zurücksetzen des Passworts",
|
||||||
"resetPasswordSuccess": "Prüfe deinen Posteingang! Du solltest eine E-Mail mit Anweisungen zum Zurücksetzen deines Passworts erhalten haben.",
|
"resetPasswordSuccess": "Prüfe deinen Posteingang! Du solltest eine E-Mail mit Anweisungen zum Zurücksetzen deines Passworts erhalten haben.",
|
||||||
|
@ -473,7 +474,8 @@
|
||||||
"download": "Herunterladen",
|
"download": "Herunterladen",
|
||||||
"showMenu": "Menü anzeigen",
|
"showMenu": "Menü anzeigen",
|
||||||
"hideMenu": "Menü ausblenden",
|
"hideMenu": "Menü ausblenden",
|
||||||
"forExample": "For example:"
|
"forExample": "For example:",
|
||||||
|
"welcomeBack": "Welcome Back!"
|
||||||
},
|
},
|
||||||
"input": {
|
"input": {
|
||||||
"resetColor": "Farbe zurücksetzen",
|
"resetColor": "Farbe zurücksetzen",
|
||||||
|
@ -811,7 +813,7 @@
|
||||||
"url": "Vikunja-URL",
|
"url": "Vikunja-URL",
|
||||||
"urlPlaceholder": "z.B. https://localhost:3456",
|
"urlPlaceholder": "z.B. https://localhost:3456",
|
||||||
"change": "ändern",
|
"change": "ändern",
|
||||||
"signInOn": "Melde dich bei deinem Vikunja-Account auf {0} an",
|
"use": "Using Vikunja installation at {0}",
|
||||||
"error": "Konnte keine Vikunja-Installation unter „{domain}“ finden oder verwenden. Bitte probiere eine andere Url.",
|
"error": "Konnte keine Vikunja-Installation unter „{domain}“ finden oder verwenden. Bitte probiere eine andere Url.",
|
||||||
"success": "Verwende die Vikunja-Installation unter „{domain}“.",
|
"success": "Verwende die Vikunja-Installation unter „{domain}“.",
|
||||||
"urlRequired": "Eine Url ist erforderlich."
|
"urlRequired": "Eine Url ist erforderlich."
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
"password": "Passwort",
|
"password": "Passwort",
|
||||||
"passwordRepeat": "Gib dis Passwort nomal iih",
|
"passwordRepeat": "Gib dis Passwort nomal iih",
|
||||||
"passwordPlaceholder": "z.B. •••••••••••",
|
"passwordPlaceholder": "z.B. •••••••••••",
|
||||||
|
"forgotPassword": "Forgot your password?",
|
||||||
"resetPassword": "Setz diis Passwort zrugg",
|
"resetPassword": "Setz diis Passwort zrugg",
|
||||||
"resetPasswordAction": "Schick mir en Passwort zruggsetz Link",
|
"resetPasswordAction": "Schick mir en Passwort zruggsetz Link",
|
||||||
"resetPasswordSuccess": "Prüfe deinen Posteingang! Du solltest eine E-Mail mit Anweisungen zum Zurücksetzen deines Passworts erhalten haben.",
|
"resetPasswordSuccess": "Prüfe deinen Posteingang! Du solltest eine E-Mail mit Anweisungen zum Zurücksetzen deines Passworts erhalten haben.",
|
||||||
|
@ -473,7 +474,8 @@
|
||||||
"download": "Herunterladen",
|
"download": "Herunterladen",
|
||||||
"showMenu": "Menü anzeigen",
|
"showMenu": "Menü anzeigen",
|
||||||
"hideMenu": "Menü ausblenden",
|
"hideMenu": "Menü ausblenden",
|
||||||
"forExample": "For example:"
|
"forExample": "For example:",
|
||||||
|
"welcomeBack": "Welcome Back!"
|
||||||
},
|
},
|
||||||
"input": {
|
"input": {
|
||||||
"resetColor": "Farb zruggsetze",
|
"resetColor": "Farb zruggsetze",
|
||||||
|
@ -811,7 +813,7 @@
|
||||||
"url": "Vikunja URL",
|
"url": "Vikunja URL",
|
||||||
"urlPlaceholder": "z.B. https://localhost:3456",
|
"urlPlaceholder": "z.B. https://localhost:3456",
|
||||||
"change": "ändere",
|
"change": "ändere",
|
||||||
"signInOn": "Dich i diin Vikunja-Account Iihloge uf {0}",
|
"use": "Using Vikunja installation at {0}",
|
||||||
"error": "Konnte keine Vikunja-Installation unter „{domain}“ finden oder verwenden. Bitte probiere eine andere Url.",
|
"error": "Konnte keine Vikunja-Installation unter „{domain}“ finden oder verwenden. Bitte probiere eine andere Url.",
|
||||||
"success": "Benutze d'Vikunja Installation uf \"{domain}\".",
|
"success": "Benutze d'Vikunja Installation uf \"{domain}\".",
|
||||||
"urlRequired": "Eine Url ist erforderlich."
|
"urlRequired": "Eine Url ist erforderlich."
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
"password": "Password",
|
"password": "Password",
|
||||||
"passwordRepeat": "Retype your password",
|
"passwordRepeat": "Retype your password",
|
||||||
"passwordPlaceholder": "e.g. •••••••••••",
|
"passwordPlaceholder": "e.g. •••••••••••",
|
||||||
|
"forgotPassword": "Forgot your password?",
|
||||||
"resetPassword": "Reset your password",
|
"resetPassword": "Reset your password",
|
||||||
"resetPasswordAction": "Send me a password reset link",
|
"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.",
|
"resetPasswordSuccess": "Check your inbox! You should have an e-mail with instructions on how to reset your password.",
|
||||||
|
@ -473,7 +474,8 @@
|
||||||
"download": "Download",
|
"download": "Download",
|
||||||
"showMenu": "Show the menu",
|
"showMenu": "Show the menu",
|
||||||
"hideMenu": "Hide the menu",
|
"hideMenu": "Hide the menu",
|
||||||
"forExample": "For example:"
|
"forExample": "For example:",
|
||||||
|
"welcomeBack": "Welcome Back!"
|
||||||
},
|
},
|
||||||
"input": {
|
"input": {
|
||||||
"resetColor": "Reset Color",
|
"resetColor": "Reset Color",
|
||||||
|
@ -811,7 +813,7 @@
|
||||||
"url": "Vikunja URL",
|
"url": "Vikunja URL",
|
||||||
"urlPlaceholder": "eg. https://localhost:3456",
|
"urlPlaceholder": "eg. https://localhost:3456",
|
||||||
"change": "change",
|
"change": "change",
|
||||||
"signInOn": "Sign in to your Vikunja account on {0}",
|
"use": "Using Vikunja installation at {0}",
|
||||||
"error": "Could not find or use Vikunja installation at \"{domain}\". Please try a different url.",
|
"error": "Could not find or use Vikunja installation at \"{domain}\". Please try a different url.",
|
||||||
"success": "Using Vikunja installation at \"{domain}\".",
|
"success": "Using Vikunja installation at \"{domain}\".",
|
||||||
"urlRequired": "A url is required."
|
"urlRequired": "A url is required."
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
"password": "Password",
|
"password": "Password",
|
||||||
"passwordRepeat": "Retype your password",
|
"passwordRepeat": "Retype your password",
|
||||||
"passwordPlaceholder": "e.g. •••••••••••",
|
"passwordPlaceholder": "e.g. •••••••••••",
|
||||||
|
"forgotPassword": "Forgot your password?",
|
||||||
"resetPassword": "Reset your password",
|
"resetPassword": "Reset your password",
|
||||||
"resetPasswordAction": "Send me a password reset link",
|
"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.",
|
"resetPasswordSuccess": "Check your inbox! You should have an e-mail with instructions on how to reset your password.",
|
||||||
|
@ -473,7 +474,8 @@
|
||||||
"download": "Download",
|
"download": "Download",
|
||||||
"showMenu": "Show the menu",
|
"showMenu": "Show the menu",
|
||||||
"hideMenu": "Hide the menu",
|
"hideMenu": "Hide the menu",
|
||||||
"forExample": "For example:"
|
"forExample": "For example:",
|
||||||
|
"welcomeBack": "Welcome Back!"
|
||||||
},
|
},
|
||||||
"input": {
|
"input": {
|
||||||
"resetColor": "Reset Color",
|
"resetColor": "Reset Color",
|
||||||
|
@ -811,7 +813,7 @@
|
||||||
"url": "Vikunja URL",
|
"url": "Vikunja URL",
|
||||||
"urlPlaceholder": "eg. https://localhost:3456",
|
"urlPlaceholder": "eg. https://localhost:3456",
|
||||||
"change": "change",
|
"change": "change",
|
||||||
"signInOn": "Sign in to your Vikunja account on {0}",
|
"use": "Using Vikunja installation at {0}",
|
||||||
"error": "Could not find or use Vikunja installation at \"{domain}\". Please try a different url.",
|
"error": "Could not find or use Vikunja installation at \"{domain}\". Please try a different url.",
|
||||||
"success": "Using Vikunja installation at \"{domain}\".",
|
"success": "Using Vikunja installation at \"{domain}\".",
|
||||||
"urlRequired": "A url is required."
|
"urlRequired": "A url is required."
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
"password": "Mot de passe",
|
"password": "Mot de passe",
|
||||||
"passwordRepeat": "Retape ton mot de passe",
|
"passwordRepeat": "Retape ton mot de passe",
|
||||||
"passwordPlaceholder": "p. ex. •••••••••••",
|
"passwordPlaceholder": "p. ex. •••••••••••",
|
||||||
|
"forgotPassword": "Forgot your password?",
|
||||||
"resetPassword": "Réinitialiser ton mot de passe",
|
"resetPassword": "Réinitialiser ton mot de passe",
|
||||||
"resetPasswordAction": "M’envoyer un lien de réinitialisation du mot de passe",
|
"resetPasswordAction": "M’envoyer un lien de réinitialisation du mot de passe",
|
||||||
"resetPasswordSuccess": "Vérifie ta boîte de réception ! Tu devrais avoir un courriel contenant les instructions sur la manière de réinitialiser ton mot de passe.",
|
"resetPasswordSuccess": "Vérifie ta boîte de réception ! Tu devrais avoir un courriel contenant les instructions sur la manière de réinitialiser ton mot de passe.",
|
||||||
|
@ -473,7 +474,8 @@
|
||||||
"download": "Télécharger",
|
"download": "Télécharger",
|
||||||
"showMenu": "Afficher le menu",
|
"showMenu": "Afficher le menu",
|
||||||
"hideMenu": "Masquer le menu",
|
"hideMenu": "Masquer le menu",
|
||||||
"forExample": "For example:"
|
"forExample": "For example:",
|
||||||
|
"welcomeBack": "Welcome Back!"
|
||||||
},
|
},
|
||||||
"input": {
|
"input": {
|
||||||
"resetColor": "Réinitialiser la couleur",
|
"resetColor": "Réinitialiser la couleur",
|
||||||
|
@ -811,7 +813,7 @@
|
||||||
"url": "URL Vikunja",
|
"url": "URL Vikunja",
|
||||||
"urlPlaceholder": "Par exemple : https://localhost:3456",
|
"urlPlaceholder": "Par exemple : https://localhost:3456",
|
||||||
"change": "changer",
|
"change": "changer",
|
||||||
"signInOn": "Se connecter à ton compte Vikunja sur {0}",
|
"use": "Using Vikunja installation at {0}",
|
||||||
"error": "Impossible de trouver ou d'utiliser l'installation de Vikunja sur « {domain} ». Veuillez essayer une autre URL.",
|
"error": "Impossible de trouver ou d'utiliser l'installation de Vikunja sur « {domain} ». Veuillez essayer une autre URL.",
|
||||||
"success": "Utilisation de l’installation Vikunja à « {domain} ».",
|
"success": "Utilisation de l’installation Vikunja à « {domain} ».",
|
||||||
"urlRequired": "Une URL est requise."
|
"urlRequired": "Une URL est requise."
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
"password": "Password",
|
"password": "Password",
|
||||||
"passwordRepeat": "Digita di nuovo la tua password",
|
"passwordRepeat": "Digita di nuovo la tua password",
|
||||||
"passwordPlaceholder": "es. ••••••••••••",
|
"passwordPlaceholder": "es. ••••••••••••",
|
||||||
|
"forgotPassword": "Forgot your password?",
|
||||||
"resetPassword": "Reimposta la tua password",
|
"resetPassword": "Reimposta la tua password",
|
||||||
"resetPasswordAction": "Inviami il link per reimpostare la password",
|
"resetPasswordAction": "Inviami il link per reimpostare la password",
|
||||||
"resetPasswordSuccess": "Controlla la tua casella di posta! Dovresti avere un'e-mail con le istruzioni su come reimpostare la password.",
|
"resetPasswordSuccess": "Controlla la tua casella di posta! Dovresti avere un'e-mail con le istruzioni su come reimpostare la password.",
|
||||||
|
@ -473,7 +474,8 @@
|
||||||
"download": "Scarica",
|
"download": "Scarica",
|
||||||
"showMenu": "Show the menu",
|
"showMenu": "Show the menu",
|
||||||
"hideMenu": "Hide the menu",
|
"hideMenu": "Hide the menu",
|
||||||
"forExample": "For example:"
|
"forExample": "For example:",
|
||||||
|
"welcomeBack": "Welcome Back!"
|
||||||
},
|
},
|
||||||
"input": {
|
"input": {
|
||||||
"resetColor": "Ripristina Colore",
|
"resetColor": "Ripristina Colore",
|
||||||
|
@ -811,7 +813,7 @@
|
||||||
"url": "URL Vikunja",
|
"url": "URL Vikunja",
|
||||||
"urlPlaceholder": "es. http://localhost:8080",
|
"urlPlaceholder": "es. http://localhost:8080",
|
||||||
"change": "change",
|
"change": "change",
|
||||||
"signInOn": "Sign in to your Vikunja account on {0}",
|
"use": "Using Vikunja installation at {0}",
|
||||||
"error": "Could not find or use Vikunja installation at \"{domain}\". Please try a different url.",
|
"error": "Could not find or use Vikunja installation at \"{domain}\". Please try a different url.",
|
||||||
"success": "Using Vikunja installation at \"{domain}\".",
|
"success": "Using Vikunja installation at \"{domain}\".",
|
||||||
"urlRequired": "A url is required."
|
"urlRequired": "A url is required."
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
"password": "Password",
|
"password": "Password",
|
||||||
"passwordRepeat": "Retype your password",
|
"passwordRepeat": "Retype your password",
|
||||||
"passwordPlaceholder": "e.g. •••••••••••",
|
"passwordPlaceholder": "e.g. •••••••••••",
|
||||||
|
"forgotPassword": "Forgot your password?",
|
||||||
"resetPassword": "Reset your password",
|
"resetPassword": "Reset your password",
|
||||||
"resetPasswordAction": "Send me a password reset link",
|
"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.",
|
"resetPasswordSuccess": "Check your inbox! You should have an e-mail with instructions on how to reset your password.",
|
||||||
|
@ -473,7 +474,8 @@
|
||||||
"download": "Download",
|
"download": "Download",
|
||||||
"showMenu": "Show the menu",
|
"showMenu": "Show the menu",
|
||||||
"hideMenu": "Hide the menu",
|
"hideMenu": "Hide the menu",
|
||||||
"forExample": "For example:"
|
"forExample": "For example:",
|
||||||
|
"welcomeBack": "Welcome Back!"
|
||||||
},
|
},
|
||||||
"input": {
|
"input": {
|
||||||
"resetColor": "Reset Color",
|
"resetColor": "Reset Color",
|
||||||
|
@ -811,7 +813,7 @@
|
||||||
"url": "Vikunja URL",
|
"url": "Vikunja URL",
|
||||||
"urlPlaceholder": "eg. https://localhost:3456",
|
"urlPlaceholder": "eg. https://localhost:3456",
|
||||||
"change": "change",
|
"change": "change",
|
||||||
"signInOn": "Sign in to your Vikunja account on {0}",
|
"use": "Using Vikunja installation at {0}",
|
||||||
"error": "Could not find or use Vikunja installation at \"{domain}\". Please try a different url.",
|
"error": "Could not find or use Vikunja installation at \"{domain}\". Please try a different url.",
|
||||||
"success": "Using Vikunja installation at \"{domain}\".",
|
"success": "Using Vikunja installation at \"{domain}\".",
|
||||||
"urlRequired": "A url is required."
|
"urlRequired": "A url is required."
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
"password": "Password",
|
"password": "Password",
|
||||||
"passwordRepeat": "Retype your password",
|
"passwordRepeat": "Retype your password",
|
||||||
"passwordPlaceholder": "e.g. •••••••••••",
|
"passwordPlaceholder": "e.g. •••••••••••",
|
||||||
|
"forgotPassword": "Forgot your password?",
|
||||||
"resetPassword": "Reset your password",
|
"resetPassword": "Reset your password",
|
||||||
"resetPasswordAction": "Send me a password reset link",
|
"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.",
|
"resetPasswordSuccess": "Check your inbox! You should have an e-mail with instructions on how to reset your password.",
|
||||||
|
@ -473,7 +474,8 @@
|
||||||
"download": "Download",
|
"download": "Download",
|
||||||
"showMenu": "Show the menu",
|
"showMenu": "Show the menu",
|
||||||
"hideMenu": "Hide the menu",
|
"hideMenu": "Hide the menu",
|
||||||
"forExample": "For example:"
|
"forExample": "For example:",
|
||||||
|
"welcomeBack": "Welcome Back!"
|
||||||
},
|
},
|
||||||
"input": {
|
"input": {
|
||||||
"resetColor": "Reset Color",
|
"resetColor": "Reset Color",
|
||||||
|
@ -811,7 +813,7 @@
|
||||||
"url": "Vikunja URL",
|
"url": "Vikunja URL",
|
||||||
"urlPlaceholder": "eg. https://localhost:3456",
|
"urlPlaceholder": "eg. https://localhost:3456",
|
||||||
"change": "change",
|
"change": "change",
|
||||||
"signInOn": "Sign in to your Vikunja account on {0}",
|
"use": "Using Vikunja installation at {0}",
|
||||||
"error": "Could not find or use Vikunja installation at \"{domain}\". Please try a different url.",
|
"error": "Could not find or use Vikunja installation at \"{domain}\". Please try a different url.",
|
||||||
"success": "Using Vikunja installation at \"{domain}\".",
|
"success": "Using Vikunja installation at \"{domain}\".",
|
||||||
"urlRequired": "A url is required."
|
"urlRequired": "A url is required."
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
"password": "Password",
|
"password": "Password",
|
||||||
"passwordRepeat": "Retype your password",
|
"passwordRepeat": "Retype your password",
|
||||||
"passwordPlaceholder": "e.g. •••••••••••",
|
"passwordPlaceholder": "e.g. •••••••••••",
|
||||||
|
"forgotPassword": "Forgot your password?",
|
||||||
"resetPassword": "Reset your password",
|
"resetPassword": "Reset your password",
|
||||||
"resetPasswordAction": "Send me a password reset link",
|
"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.",
|
"resetPasswordSuccess": "Check your inbox! You should have an e-mail with instructions on how to reset your password.",
|
||||||
|
@ -473,7 +474,8 @@
|
||||||
"download": "Download",
|
"download": "Download",
|
||||||
"showMenu": "Show the menu",
|
"showMenu": "Show the menu",
|
||||||
"hideMenu": "Hide the menu",
|
"hideMenu": "Hide the menu",
|
||||||
"forExample": "For example:"
|
"forExample": "For example:",
|
||||||
|
"welcomeBack": "Welcome Back!"
|
||||||
},
|
},
|
||||||
"input": {
|
"input": {
|
||||||
"resetColor": "Reset Color",
|
"resetColor": "Reset Color",
|
||||||
|
@ -811,7 +813,7 @@
|
||||||
"url": "Vikunja URL",
|
"url": "Vikunja URL",
|
||||||
"urlPlaceholder": "eg. https://localhost:3456",
|
"urlPlaceholder": "eg. https://localhost:3456",
|
||||||
"change": "change",
|
"change": "change",
|
||||||
"signInOn": "Sign in to your Vikunja account on {0}",
|
"use": "Using Vikunja installation at {0}",
|
||||||
"error": "Could not find or use Vikunja installation at \"{domain}\". Please try a different url.",
|
"error": "Could not find or use Vikunja installation at \"{domain}\". Please try a different url.",
|
||||||
"success": "Using Vikunja installation at \"{domain}\".",
|
"success": "Using Vikunja installation at \"{domain}\".",
|
||||||
"urlRequired": "A url is required."
|
"urlRequired": "A url is required."
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
"password": "Password",
|
"password": "Password",
|
||||||
"passwordRepeat": "Retype your password",
|
"passwordRepeat": "Retype your password",
|
||||||
"passwordPlaceholder": "e.g. •••••••••••",
|
"passwordPlaceholder": "e.g. •••••••••••",
|
||||||
|
"forgotPassword": "Forgot your password?",
|
||||||
"resetPassword": "Reset your password",
|
"resetPassword": "Reset your password",
|
||||||
"resetPasswordAction": "Send me a password reset link",
|
"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.",
|
"resetPasswordSuccess": "Check your inbox! You should have an e-mail with instructions on how to reset your password.",
|
||||||
|
@ -473,7 +474,8 @@
|
||||||
"download": "Download",
|
"download": "Download",
|
||||||
"showMenu": "Show the menu",
|
"showMenu": "Show the menu",
|
||||||
"hideMenu": "Hide the menu",
|
"hideMenu": "Hide the menu",
|
||||||
"forExample": "For example:"
|
"forExample": "For example:",
|
||||||
|
"welcomeBack": "Welcome Back!"
|
||||||
},
|
},
|
||||||
"input": {
|
"input": {
|
||||||
"resetColor": "Reset Color",
|
"resetColor": "Reset Color",
|
||||||
|
@ -811,7 +813,7 @@
|
||||||
"url": "Vikunja URL",
|
"url": "Vikunja URL",
|
||||||
"urlPlaceholder": "eg. https://localhost:3456",
|
"urlPlaceholder": "eg. https://localhost:3456",
|
||||||
"change": "change",
|
"change": "change",
|
||||||
"signInOn": "Sign in to your Vikunja account on {0}",
|
"use": "Using Vikunja installation at {0}",
|
||||||
"error": "Could not find or use Vikunja installation at \"{domain}\". Please try a different url.",
|
"error": "Could not find or use Vikunja installation at \"{domain}\". Please try a different url.",
|
||||||
"success": "Using Vikunja installation at \"{domain}\".",
|
"success": "Using Vikunja installation at \"{domain}\".",
|
||||||
"urlRequired": "A url is required."
|
"urlRequired": "A url is required."
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
"password": "Пароль",
|
"password": "Пароль",
|
||||||
"passwordRepeat": "Пароль ещё раз",
|
"passwordRepeat": "Пароль ещё раз",
|
||||||
"passwordPlaceholder": "напр. •••••••••••",
|
"passwordPlaceholder": "напр. •••••••••••",
|
||||||
|
"forgotPassword": "Forgot your password?",
|
||||||
"resetPassword": "Сбросить пароль",
|
"resetPassword": "Сбросить пароль",
|
||||||
"resetPasswordAction": "Отправить ссылку на сброс пароля",
|
"resetPasswordAction": "Отправить ссылку на сброс пароля",
|
||||||
"resetPasswordSuccess": "Проверь свою почту! Там должно быть письмо с инструкциями, как сбросить пароль.",
|
"resetPasswordSuccess": "Проверь свою почту! Там должно быть письмо с инструкциями, как сбросить пароль.",
|
||||||
|
@ -473,7 +474,8 @@
|
||||||
"download": "Скачать",
|
"download": "Скачать",
|
||||||
"showMenu": "Show the menu",
|
"showMenu": "Show the menu",
|
||||||
"hideMenu": "Hide the menu",
|
"hideMenu": "Hide the menu",
|
||||||
"forExample": "For example:"
|
"forExample": "For example:",
|
||||||
|
"welcomeBack": "Welcome Back!"
|
||||||
},
|
},
|
||||||
"input": {
|
"input": {
|
||||||
"resetColor": "Сбросить цвет",
|
"resetColor": "Сбросить цвет",
|
||||||
|
@ -811,7 +813,7 @@
|
||||||
"url": "Vikunja URL",
|
"url": "Vikunja URL",
|
||||||
"urlPlaceholder": "напр. https://localhost:3456",
|
"urlPlaceholder": "напр. https://localhost:3456",
|
||||||
"change": "изменить",
|
"change": "изменить",
|
||||||
"signInOn": "Войди в свой аккаунт Vikunja на {0}",
|
"use": "Using Vikunja installation at {0}",
|
||||||
"error": "Could not find or use Vikunja installation at \"{domain}\". Please try a different url.",
|
"error": "Could not find or use Vikunja installation at \"{domain}\". Please try a different url.",
|
||||||
"success": "Используется Vikunja на \"{domain}\".",
|
"success": "Используется Vikunja на \"{domain}\".",
|
||||||
"urlRequired": "A url is required."
|
"urlRequired": "A url is required."
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
"password": "Password",
|
"password": "Password",
|
||||||
"passwordRepeat": "Retype your password",
|
"passwordRepeat": "Retype your password",
|
||||||
"passwordPlaceholder": "e.g. •••••••••••",
|
"passwordPlaceholder": "e.g. •••••••••••",
|
||||||
|
"forgotPassword": "Forgot your password?",
|
||||||
"resetPassword": "Reset your password",
|
"resetPassword": "Reset your password",
|
||||||
"resetPasswordAction": "Send me a password reset link",
|
"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.",
|
"resetPasswordSuccess": "Check your inbox! You should have an e-mail with instructions on how to reset your password.",
|
||||||
|
@ -473,7 +474,8 @@
|
||||||
"download": "Download",
|
"download": "Download",
|
||||||
"showMenu": "Show the menu",
|
"showMenu": "Show the menu",
|
||||||
"hideMenu": "Hide the menu",
|
"hideMenu": "Hide the menu",
|
||||||
"forExample": "For example:"
|
"forExample": "For example:",
|
||||||
|
"welcomeBack": "Welcome Back!"
|
||||||
},
|
},
|
||||||
"input": {
|
"input": {
|
||||||
"resetColor": "Reset Color",
|
"resetColor": "Reset Color",
|
||||||
|
@ -811,7 +813,7 @@
|
||||||
"url": "Vikunja URL",
|
"url": "Vikunja URL",
|
||||||
"urlPlaceholder": "eg. https://localhost:3456",
|
"urlPlaceholder": "eg. https://localhost:3456",
|
||||||
"change": "change",
|
"change": "change",
|
||||||
"signInOn": "Sign in to your Vikunja account on {0}",
|
"use": "Using Vikunja installation at {0}",
|
||||||
"error": "Could not find or use Vikunja installation at \"{domain}\". Please try a different url.",
|
"error": "Could not find or use Vikunja installation at \"{domain}\". Please try a different url.",
|
||||||
"success": "Using Vikunja installation at \"{domain}\".",
|
"success": "Using Vikunja installation at \"{domain}\".",
|
||||||
"urlRequired": "A url is required."
|
"urlRequired": "A url is required."
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
"password": "Password",
|
"password": "Password",
|
||||||
"passwordRepeat": "Retype your password",
|
"passwordRepeat": "Retype your password",
|
||||||
"passwordPlaceholder": "e.g. •••••••••••",
|
"passwordPlaceholder": "e.g. •••••••••••",
|
||||||
|
"forgotPassword": "Forgot your password?",
|
||||||
"resetPassword": "Reset your password",
|
"resetPassword": "Reset your password",
|
||||||
"resetPasswordAction": "Send me a password reset link",
|
"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.",
|
"resetPasswordSuccess": "Check your inbox! You should have an e-mail with instructions on how to reset your password.",
|
||||||
|
@ -473,7 +474,8 @@
|
||||||
"download": "Download",
|
"download": "Download",
|
||||||
"showMenu": "Show the menu",
|
"showMenu": "Show the menu",
|
||||||
"hideMenu": "Hide the menu",
|
"hideMenu": "Hide the menu",
|
||||||
"forExample": "For example:"
|
"forExample": "For example:",
|
||||||
|
"welcomeBack": "Welcome Back!"
|
||||||
},
|
},
|
||||||
"input": {
|
"input": {
|
||||||
"resetColor": "Reset Color",
|
"resetColor": "Reset Color",
|
||||||
|
@ -811,7 +813,7 @@
|
||||||
"url": "Vikunja URL",
|
"url": "Vikunja URL",
|
||||||
"urlPlaceholder": "eg. https://localhost:3456",
|
"urlPlaceholder": "eg. https://localhost:3456",
|
||||||
"change": "change",
|
"change": "change",
|
||||||
"signInOn": "Sign in to your Vikunja account on {0}",
|
"use": "Using Vikunja installation at {0}",
|
||||||
"error": "Could not find or use Vikunja installation at \"{domain}\". Please try a different url.",
|
"error": "Could not find or use Vikunja installation at \"{domain}\". Please try a different url.",
|
||||||
"success": "Using Vikunja installation at \"{domain}\".",
|
"success": "Using Vikunja installation at \"{domain}\".",
|
||||||
"urlRequired": "A url is required."
|
"urlRequired": "A url is required."
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
"password": "Mật khẩu",
|
"password": "Mật khẩu",
|
||||||
"passwordRepeat": "Nhập lại mật khẩu",
|
"passwordRepeat": "Nhập lại mật khẩu",
|
||||||
"passwordPlaceholder": "ví dụ: •••••••••••",
|
"passwordPlaceholder": "ví dụ: •••••••••••",
|
||||||
|
"forgotPassword": "Forgot your password?",
|
||||||
"resetPassword": "Reset mật khẩu của bạn",
|
"resetPassword": "Reset mật khẩu của bạn",
|
||||||
"resetPasswordAction": "Gửi cho tôi liên kết reset mật khẩu",
|
"resetPasswordAction": "Gửi cho tôi liên kết reset mật khẩu",
|
||||||
"resetPasswordSuccess": "Kiểm tra hộp thư của bạn! Bạn sẽ nhận một e-mail với hướng dẫn reset mật khẩu của mình.",
|
"resetPasswordSuccess": "Kiểm tra hộp thư của bạn! Bạn sẽ nhận một e-mail với hướng dẫn reset mật khẩu của mình.",
|
||||||
|
@ -473,7 +474,8 @@
|
||||||
"download": "Tải về",
|
"download": "Tải về",
|
||||||
"showMenu": "Hiển thị menu",
|
"showMenu": "Hiển thị menu",
|
||||||
"hideMenu": "Ẩn menu",
|
"hideMenu": "Ẩn menu",
|
||||||
"forExample": "For example:"
|
"forExample": "For example:",
|
||||||
|
"welcomeBack": "Welcome Back!"
|
||||||
},
|
},
|
||||||
"input": {
|
"input": {
|
||||||
"resetColor": "Đặt lại màu",
|
"resetColor": "Đặt lại màu",
|
||||||
|
@ -811,7 +813,7 @@
|
||||||
"url": "URL Vikunja",
|
"url": "URL Vikunja",
|
||||||
"urlPlaceholder": "ví dụ: https://localhost:3456",
|
"urlPlaceholder": "ví dụ: https://localhost:3456",
|
||||||
"change": "thay đổi",
|
"change": "thay đổi",
|
||||||
"signInOn": "Đăng nhập vào tài khoản Vikunia tại {0}",
|
"use": "Using Vikunja installation at {0}",
|
||||||
"error": "Không thể tìm thấy hoặc sử dụng cài đặt Vikunja tại \"{domain}\". Vui lòng thử một url khác.",
|
"error": "Không thể tìm thấy hoặc sử dụng cài đặt Vikunja tại \"{domain}\". Vui lòng thử một url khác.",
|
||||||
"success": "Sử dụng cài đặt Vikunja tại \"{domain}\".",
|
"success": "Sử dụng cài đặt Vikunja tại \"{domain}\".",
|
||||||
"urlRequired": "Cần có một url."
|
"urlRequired": "Cần có một url."
|
||||||
|
|
|
@ -105,21 +105,33 @@ const router = createRouter({
|
||||||
path: '/login',
|
path: '/login',
|
||||||
name: 'user.login',
|
name: 'user.login',
|
||||||
component: LoginComponent,
|
component: LoginComponent,
|
||||||
|
meta: {
|
||||||
|
title: 'user.auth.login',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/get-password-reset',
|
path: '/get-password-reset',
|
||||||
name: 'user.password-reset.request',
|
name: 'user.password-reset.request',
|
||||||
component: GetPasswordResetComponent,
|
component: GetPasswordResetComponent,
|
||||||
|
meta: {
|
||||||
|
title: 'user.auth.resetPassword',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/password-reset',
|
path: '/password-reset',
|
||||||
name: 'user.password-reset.reset',
|
name: 'user.password-reset.reset',
|
||||||
component: PasswordResetComponent,
|
component: PasswordResetComponent,
|
||||||
|
meta: {
|
||||||
|
title: 'user.auth.resetPassword',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/register',
|
path: '/register',
|
||||||
name: 'user.register',
|
name: 'user.register',
|
||||||
component: RegisterComponent,
|
component: RegisterComponent,
|
||||||
|
meta: {
|
||||||
|
title: 'user.auth.register',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/user/settings',
|
path: '/user/settings',
|
||||||
|
|
|
@ -28,7 +28,7 @@ export default class AbstractService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The abstract constructor.
|
* The abstract constructor.
|
||||||
* @param paths An object with all paths. Default values are specified above.
|
* @param [paths] An object with all paths. Default values are specified above.
|
||||||
*/
|
*/
|
||||||
constructor(paths) {
|
constructor(paths) {
|
||||||
this.http = axios.create({
|
this.http = axios.create({
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<h2 v-if="userInfo">
|
<h2 v-if="userInfo">
|
||||||
{{ $t(`home.welcome${welcome}`, {username: userInfo.name !== '' ? userInfo.name : userInfo.username}) }}!
|
{{ $t(`home.welcome${welcome}`, {username: userInfo.name !== '' ? userInfo.name : userInfo.username}) }}!
|
||||||
</h2>
|
</h2>
|
||||||
<message variant="danger" v-if="deletionScheduledAt !== null">
|
<message variant="danger" v-if="deletionScheduledAt !== null" class="mb-4">
|
||||||
{{
|
{{
|
||||||
$t('user.deletion.scheduled', {
|
$t('user.deletion.scheduled', {
|
||||||
date: formatDateShort(deletionScheduledAt),
|
date: formatDateShort(deletionScheduledAt),
|
||||||
|
|
|
@ -16,83 +16,101 @@
|
||||||
:placeholder="$t('user.auth.passwordPlaceholder')"
|
:placeholder="$t('user.auth.passwordPlaceholder')"
|
||||||
v-model="password"
|
v-model="password"
|
||||||
v-focus
|
v-focus
|
||||||
@keyup.enter.prevent="auth"
|
@keyup.enter.prevent="authenticate()"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<x-button @click="auth" :loading="loading">
|
<x-button @click="authenticate()" :loading="loading">
|
||||||
{{ $t('user.auth.login') }}
|
{{ $t('user.auth.login') }}
|
||||||
</x-button>
|
</x-button>
|
||||||
|
|
||||||
<message variant="danger" class="mt-4" v-if="errorMessage !== ''">
|
<Message variant="danger" class="mt-4" v-if="errorMessage !== ''">
|
||||||
{{ errorMessage }}
|
{{ errorMessage }}
|
||||||
</message>
|
</Message>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts" setup>
|
||||||
import {mapGetters} from 'vuex'
|
import {ref, computed} from 'vue'
|
||||||
import Message from '@/components/misc/message'
|
import {useStore} from 'vuex'
|
||||||
|
import {useRoute, useRouter} from 'vue-router'
|
||||||
|
import {useI18n} from 'vue-i18n'
|
||||||
|
import {useTitle} from '@vueuse/core'
|
||||||
|
|
||||||
export default {
|
import Message from '@/components/misc/message.vue'
|
||||||
name: 'LinkSharingAuth',
|
|
||||||
components: {Message},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
loading: true,
|
|
||||||
authenticateWithPassword: false,
|
|
||||||
errorMessage: '',
|
|
||||||
|
|
||||||
hash: '',
|
const {t} = useI18n()
|
||||||
password: '',
|
useTitle(t('sharing.authenticating'))
|
||||||
|
|
||||||
|
async function useAuth() {
|
||||||
|
const store = useStore()
|
||||||
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
const authenticateWithPassword = ref(false)
|
||||||
|
const errorMessage = ref('')
|
||||||
|
const password = ref('')
|
||||||
|
|
||||||
|
const authLinkShare = computed(() => store.getters['auth/authLinkShare'])
|
||||||
|
|
||||||
|
async function authenticate() {
|
||||||
|
authenticateWithPassword.value = false
|
||||||
|
errorMessage.value = ''
|
||||||
|
|
||||||
|
if (authLinkShare.value) {
|
||||||
|
// FIXME: push to 'list.list' since authenticated?
|
||||||
|
return
|
||||||
}
|
}
|
||||||
},
|
|
||||||
created() {
|
// TODO: no password
|
||||||
this.auth()
|
|
||||||
},
|
loading.value = true
|
||||||
mounted() {
|
|
||||||
this.setTitle(this.$t('sharing.authenticating'))
|
|
||||||
},
|
|
||||||
computed: mapGetters('auth', [
|
|
||||||
'authLinkShare',
|
|
||||||
]),
|
|
||||||
methods: {
|
|
||||||
async auth() {
|
|
||||||
this.errorMessage = ''
|
|
||||||
|
|
||||||
if (this.authLinkShare) {
|
try {
|
||||||
|
const {list_id: listId} = await store.dispatch('auth/linkShareAuth', {
|
||||||
|
hash: route.params.share,
|
||||||
|
password: password.value,
|
||||||
|
})
|
||||||
|
router.push({name: 'list.list', params: {listId}})
|
||||||
|
} catch (e) {
|
||||||
|
if (e.response?.data?.code === 13001) {
|
||||||
|
authenticateWithPassword.value = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.loading = true
|
// TODO: Put this logic in a global errorMessage handler method which checks all auth codes
|
||||||
|
let errorMessage = t('sharing.error')
|
||||||
try {
|
if (e.response?.data?.message) {
|
||||||
const r = await this.$store.dispatch('auth/linkShareAuth', {
|
errorMessage = e.response.data.message
|
||||||
hash: this.$route.params.share,
|
|
||||||
password: this.password,
|
|
||||||
})
|
|
||||||
this.$router.push({name: 'list.list', params: {listId: r.list_id}})
|
|
||||||
} catch (e) {
|
|
||||||
if (typeof e.response.data.code !== 'undefined' && e.response.data.code === 13001) {
|
|
||||||
this.authenticateWithPassword = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Put this logic in a global errorMessage handler method which checks all auth codes
|
|
||||||
let errorMessage = this.$t('sharing.error')
|
|
||||||
if (e.response && e.response.data && e.response.data.message) {
|
|
||||||
errorMessage = e.response.data.message
|
|
||||||
}
|
|
||||||
if (typeof e.response.data.code !== 'undefined' && e.response.data.code === 13002) {
|
|
||||||
errorMessage = this.$t('sharing.invalidPassword')
|
|
||||||
}
|
|
||||||
this.errorMessage = errorMessage
|
|
||||||
} finally {
|
|
||||||
this.loading = false
|
|
||||||
}
|
}
|
||||||
},
|
if (e.response?.data?.code === 13002) {
|
||||||
},
|
errorMessage = t('sharing.invalidPassword')
|
||||||
|
}
|
||||||
|
errorMessage.value = errorMessage
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
authenticate()
|
||||||
|
|
||||||
|
return {
|
||||||
|
loading,
|
||||||
|
authenticateWithPassword,
|
||||||
|
errorMessage,
|
||||||
|
password,
|
||||||
|
authenticate,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
loading,
|
||||||
|
authenticateWithPassword,
|
||||||
|
errorMessage,
|
||||||
|
password,
|
||||||
|
authenticate,
|
||||||
|
} = useAuth()
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script lang="ts" setup>
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import ShowTasks from './ShowTasks'
|
import ShowTasks from './ShowTasks'
|
||||||
|
|
||||||
|
|
|
@ -238,7 +238,7 @@
|
||||||
</h3>
|
</h3>
|
||||||
<div class="field has-addons">
|
<div class="field has-addons">
|
||||||
<div class="control is-expanded">
|
<div class="control is-expanded">
|
||||||
<list-search @selected="changeList" ref="moveList"/>
|
<list-search @update:modelValue="changeList" ref="moveList"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,103 +1,97 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<h2 class="title has-text-centered">Login</h2>
|
<message variant="success" class="has-text-centered" v-if="confirmedEmailSuccess">
|
||||||
<div class="box">
|
{{ $t('user.auth.confirmEmailSuccess') }}
|
||||||
<message variant="success" class="has-text-centered" v-if="confirmedEmailSuccess">
|
</message>
|
||||||
{{ $t('user.auth.confirmEmailSuccess') }}
|
<message variant="danger" v-if="errorMessage">
|
||||||
</message>
|
{{ errorMessage }}
|
||||||
<api-config @foundApi="hasApiUrl = true"/>
|
</message>
|
||||||
<form @submit.prevent="submit" id="loginform" v-if="hasApiUrl && localAuthEnabled">
|
<form @submit.prevent="submit" id="loginform" v-if="localAuthEnabled">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label" for="username">{{ $t('user.auth.usernameEmail') }}</label>
|
<label class="label" for="username">{{ $t('user.auth.usernameEmail') }}</label>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input
|
<input
|
||||||
class="input" id="username"
|
class="input" id="username"
|
||||||
name="username"
|
name="username"
|
||||||
:placeholder="$t('user.auth.usernamePlaceholder')"
|
:placeholder="$t('user.auth.usernamePlaceholder')"
|
||||||
ref="username"
|
ref="username"
|
||||||
required
|
required
|
||||||
type="text"
|
type="text"
|
||||||
autocomplete="username"
|
autocomplete="username"
|
||||||
v-focus
|
v-focus
|
||||||
@keyup.enter="submit"
|
@keyup.enter="submit"
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
</div>
|
||||||
<label class="label" for="password">{{ $t('user.auth.password') }}</label>
|
<div class="field">
|
||||||
<div class="control">
|
<label class="label" for="password">{{ $t('user.auth.password') }}</label>
|
||||||
<input
|
<div class="control">
|
||||||
class="input"
|
<input
|
||||||
id="password"
|
class="input"
|
||||||
name="password"
|
id="password"
|
||||||
:placeholder="$t('user.auth.passwordPlaceholder')"
|
name="password"
|
||||||
ref="password"
|
:placeholder="$t('user.auth.passwordPlaceholder')"
|
||||||
required
|
ref="password"
|
||||||
type="password"
|
required
|
||||||
autocomplete="current-password"
|
type="password"
|
||||||
@keyup.enter="submit"
|
autocomplete="current-password"
|
||||||
/>
|
@keyup.enter="submit"
|
||||||
</div>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="field" v-if="needsTotpPasscode">
|
</div>
|
||||||
<label class="label" for="totpPasscode">{{ $t('user.auth.totpTitle') }}</label>
|
<div class="field" v-if="needsTotpPasscode">
|
||||||
<div class="control">
|
<label class="label" for="totpPasscode">{{ $t('user.auth.totpTitle') }}</label>
|
||||||
<input
|
<div class="control">
|
||||||
autocomplete="one-time-code"
|
<input
|
||||||
class="input"
|
autocomplete="one-time-code"
|
||||||
id="totpPasscode"
|
class="input"
|
||||||
:placeholder="$t('user.auth.totpPlaceholder')"
|
id="totpPasscode"
|
||||||
ref="totpPasscode"
|
:placeholder="$t('user.auth.totpPlaceholder')"
|
||||||
required
|
ref="totpPasscode"
|
||||||
type="text"
|
required
|
||||||
v-focus
|
type="text"
|
||||||
@keyup.enter="submit"
|
v-focus
|
||||||
/>
|
@keyup.enter="submit"
|
||||||
</div>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="field is-grouped login-buttons">
|
|
||||||
<div class="control is-expanded">
|
|
||||||
<x-button
|
|
||||||
@click="submit"
|
|
||||||
:loading="loading"
|
|
||||||
>
|
|
||||||
{{ $t('user.auth.login') }}
|
|
||||||
</x-button>
|
|
||||||
<x-button
|
|
||||||
:to="{ name: 'user.register' }"
|
|
||||||
v-if="registrationEnabled"
|
|
||||||
type="secondary"
|
|
||||||
>
|
|
||||||
{{ $t('user.auth.register') }}
|
|
||||||
</x-button>
|
|
||||||
</div>
|
|
||||||
<div class="control">
|
|
||||||
<router-link :to="{ name: 'user.password-reset.request' }" class="reset-password-link">
|
|
||||||
{{ $t('user.auth.resetPassword') }}
|
|
||||||
</router-link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<message variant="danger" v-if="errorMessage">
|
|
||||||
{{ errorMessage }}
|
|
||||||
</message>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<div
|
|
||||||
v-if="hasOpenIdProviders"
|
|
||||||
class="mt-4">
|
|
||||||
<x-button
|
|
||||||
@click="redirectToProvider(p)"
|
|
||||||
v-for="(p, k) in openidConnect.providers"
|
|
||||||
:key="k"
|
|
||||||
type="secondary"
|
|
||||||
class="is-fullwidth mt-2"
|
|
||||||
>
|
|
||||||
{{ $t('user.auth.loginWith', {provider: p.name}) }}
|
|
||||||
</x-button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<legal/>
|
<div class="field is-grouped login-buttons">
|
||||||
|
<div class="control is-expanded">
|
||||||
|
<x-button
|
||||||
|
@click="submit"
|
||||||
|
:loading="loading"
|
||||||
|
>
|
||||||
|
{{ $t('user.auth.login') }}
|
||||||
|
</x-button>
|
||||||
|
<x-button
|
||||||
|
:to="{ name: 'user.register' }"
|
||||||
|
v-if="registrationEnabled"
|
||||||
|
type="secondary"
|
||||||
|
>
|
||||||
|
{{ $t('user.auth.register') }}
|
||||||
|
</x-button>
|
||||||
|
</div>
|
||||||
|
<div class="control">
|
||||||
|
<router-link :to="{ name: 'user.password-reset.request' }" class="reset-password-link">
|
||||||
|
{{ $t('user.auth.forgotPassword') }}
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="hasOpenIdProviders"
|
||||||
|
class="mt-4">
|
||||||
|
<x-button
|
||||||
|
@click="redirectToProvider(p)"
|
||||||
|
v-for="(p, k) in openidConnect.providers"
|
||||||
|
:key="k"
|
||||||
|
type="secondary"
|
||||||
|
class="is-fullwidth mt-2"
|
||||||
|
>
|
||||||
|
{{ $t('user.auth.loginWith', {provider: p.name}) }}
|
||||||
|
</x-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -107,8 +101,6 @@ import {mapState} from 'vuex'
|
||||||
|
|
||||||
import {HTTPFactory} from '@/http-common'
|
import {HTTPFactory} from '@/http-common'
|
||||||
import {LOADING} from '@/store/mutation-types'
|
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 {getErrorText} from '@/message'
|
||||||
import Message from '@/components/misc/message'
|
import Message from '@/components/misc/message'
|
||||||
import {redirectToProvider} from '../../helpers/redirectToProvider'
|
import {redirectToProvider} from '../../helpers/redirectToProvider'
|
||||||
|
@ -117,13 +109,10 @@ import {getLastVisited, clearLastVisited} from '../../helpers/saveLastVisited'
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
Message,
|
Message,
|
||||||
ApiConfig,
|
|
||||||
legal,
|
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
confirmedEmailSuccess: false,
|
confirmedEmailSuccess: false,
|
||||||
hasApiUrl: false,
|
|
||||||
errorMessage: '',
|
errorMessage: '',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -161,13 +150,11 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.hasApiUrl = window.API_URL !== ''
|
|
||||||
this.setTitle(this.$t('user.auth.login'))
|
this.setTitle(this.$t('user.auth.login'))
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
hasOpenIdProviders() {
|
hasOpenIdProviders() {
|
||||||
return this.hasApiUrl &&
|
return this.openidConnect.enabled &&
|
||||||
this.openidConnect.enabled &&
|
|
||||||
this.openidConnect.providers &&
|
this.openidConnect.providers &&
|
||||||
this.openidConnect.providers.length > 0
|
this.openidConnect.providers.length > 0
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,67 +1,60 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<h2 class="title has-text-centered">{{ $t('user.auth.resetPassword') }}</h2>
|
<message v-if="errorMsg">
|
||||||
<div class="box">
|
{{ errorMsg }}
|
||||||
<form @submit.prevent="submit" id="form" v-if="!successMessage">
|
</message>
|
||||||
<div class="field">
|
<div class="has-text-centered" v-if="successMessage">
|
||||||
<label class="label" for="password1">{{ $t('user.auth.password') }}</label>
|
<message variant="success">
|
||||||
<div class="control">
|
{{ successMessage }}
|
||||||
<input
|
</message>
|
||||||
class="input"
|
<x-button :to="{ name: 'user.login' }">
|
||||||
id="password1"
|
{{ $t('user.auth.login') }}
|
||||||
name="password1"
|
</x-button>
|
||||||
:placeholder="$t('user.auth.passwordPlaceholder')"
|
|
||||||
required
|
|
||||||
type="password"
|
|
||||||
autocomplete="new-password"
|
|
||||||
v-focus
|
|
||||||
v-model="credentials.password"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<label class="label" for="password2">{{ $t('user.auth.passwordRepeat') }}</label>
|
|
||||||
<div class="control">
|
|
||||||
<input
|
|
||||||
class="input"
|
|
||||||
id="password2"
|
|
||||||
name="password2"
|
|
||||||
:placeholder="$t('user.auth.passwordPlaceholder')"
|
|
||||||
required
|
|
||||||
type="password"
|
|
||||||
autocomplete="new-password"
|
|
||||||
v-model="credentials.password2"
|
|
||||||
@keyup.enter="submit"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="field is-grouped">
|
|
||||||
<div class="control">
|
|
||||||
<x-button
|
|
||||||
:loading="this.passwordResetService.loading"
|
|
||||||
@click="submit"
|
|
||||||
>
|
|
||||||
{{ $t('user.auth.resetPassword') }}
|
|
||||||
</x-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<message v-if="this.passwordResetService.loading">
|
|
||||||
{{ $t('misc.loading') }}
|
|
||||||
</message>
|
|
||||||
<message v-if="errorMsg">
|
|
||||||
{{ errorMsg }}
|
|
||||||
</message>
|
|
||||||
</form>
|
|
||||||
<div class="has-text-centered" v-if="successMessage">
|
|
||||||
<message variant="success">
|
|
||||||
{{ successMessage }}
|
|
||||||
</message>
|
|
||||||
<x-button :to="{ name: 'user.login' }">
|
|
||||||
{{ $t('user.auth.login') }}
|
|
||||||
</x-button>
|
|
||||||
</div>
|
|
||||||
<Legal/>
|
|
||||||
</div>
|
</div>
|
||||||
|
<form @submit.prevent="submit" id="form" v-if="!successMessage">
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" for="password1">{{ $t('user.auth.password') }}</label>
|
||||||
|
<div class="control">
|
||||||
|
<input
|
||||||
|
class="input"
|
||||||
|
id="password1"
|
||||||
|
name="password1"
|
||||||
|
:placeholder="$t('user.auth.passwordPlaceholder')"
|
||||||
|
required
|
||||||
|
type="password"
|
||||||
|
autocomplete="new-password"
|
||||||
|
v-focus
|
||||||
|
v-model="credentials.password"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" for="password2">{{ $t('user.auth.passwordRepeat') }}</label>
|
||||||
|
<div class="control">
|
||||||
|
<input
|
||||||
|
class="input"
|
||||||
|
id="password2"
|
||||||
|
name="password2"
|
||||||
|
:placeholder="$t('user.auth.passwordPlaceholder')"
|
||||||
|
required
|
||||||
|
type="password"
|
||||||
|
autocomplete="new-password"
|
||||||
|
v-model="credentials.password2"
|
||||||
|
@keyup.enter="submit"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field is-grouped">
|
||||||
|
<div class="control">
|
||||||
|
<x-button
|
||||||
|
:loading="this.passwordResetService.loading"
|
||||||
|
@click="submit"
|
||||||
|
>
|
||||||
|
{{ $t('user.auth.resetPassword') }}
|
||||||
|
</x-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -69,14 +62,11 @@
|
||||||
import {ref, reactive} from 'vue'
|
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 PasswordResetModel from '@/models/passwordReset'
|
||||||
import PasswordResetService from '@/services/passwordReset'
|
import PasswordResetService from '@/services/passwordReset'
|
||||||
import {useTitle} from '@/composables/useTitle'
|
|
||||||
import Message from '@/components/misc/message'
|
import Message from '@/components/misc/message'
|
||||||
|
|
||||||
const {t} = useI18n()
|
const {t} = useI18n()
|
||||||
useTitle(() => t('user.auth.resetPassword'))
|
|
||||||
|
|
||||||
const credentials = reactive({
|
const credentials = reactive({
|
||||||
password: '',
|
password: '',
|
||||||
|
|
|
@ -1,97 +1,90 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<h2 class="title has-text-centered">{{ $t('user.auth.register') }}</h2>
|
<message variant="danger" v-if="errorMessage !== ''">
|
||||||
<div class="box">
|
{{ errorMessage }}
|
||||||
<form @submit.prevent="submit" id="registerform">
|
</message>
|
||||||
<div class="field">
|
<form @submit.prevent="submit" id="registerform">
|
||||||
<label class="label" for="username">{{ $t('user.auth.username') }}</label>
|
<div class="field">
|
||||||
<div class="control">
|
<label class="label" for="username">{{ $t('user.auth.username') }}</label>
|
||||||
<input
|
<div class="control">
|
||||||
class="input"
|
<input
|
||||||
id="username"
|
class="input"
|
||||||
name="username"
|
id="username"
|
||||||
:placeholder="$t('user.auth.usernamePlaceholder')"
|
name="username"
|
||||||
required
|
:placeholder="$t('user.auth.usernamePlaceholder')"
|
||||||
type="text"
|
required
|
||||||
autocomplete="username"
|
type="text"
|
||||||
v-focus
|
autocomplete="username"
|
||||||
v-model="credentials.username"
|
v-focus
|
||||||
@keyup.enter="submit"
|
v-model="credentials.username"
|
||||||
/>
|
@keyup.enter="submit"
|
||||||
</div>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
</div>
|
||||||
<label class="label" for="email">{{ $t('user.auth.email') }}</label>
|
<div class="field">
|
||||||
<div class="control">
|
<label class="label" for="email">{{ $t('user.auth.email') }}</label>
|
||||||
<input
|
<div class="control">
|
||||||
class="input"
|
<input
|
||||||
id="email"
|
class="input"
|
||||||
name="email"
|
id="email"
|
||||||
:placeholder="$t('user.auth.emailPlaceholder')"
|
name="email"
|
||||||
required
|
:placeholder="$t('user.auth.emailPlaceholder')"
|
||||||
type="email"
|
required
|
||||||
v-model="credentials.email"
|
type="email"
|
||||||
@keyup.enter="submit"
|
v-model="credentials.email"
|
||||||
/>
|
@keyup.enter="submit"
|
||||||
</div>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
</div>
|
||||||
<label class="label" for="password">{{ $t('user.auth.password') }}</label>
|
<div class="field">
|
||||||
<div class="control">
|
<label class="label" for="password">{{ $t('user.auth.password') }}</label>
|
||||||
<input
|
<div class="control">
|
||||||
class="input"
|
<input
|
||||||
id="password"
|
class="input"
|
||||||
name="password"
|
id="password"
|
||||||
:placeholder="$t('user.auth.passwordPlaceholder')"
|
name="password"
|
||||||
required
|
:placeholder="$t('user.auth.passwordPlaceholder')"
|
||||||
type="password"
|
required
|
||||||
autocomplete="new-password"
|
type="password"
|
||||||
v-model="credentials.password"
|
autocomplete="new-password"
|
||||||
@keyup.enter="submit"
|
v-model="credentials.password"
|
||||||
/>
|
@keyup.enter="submit"
|
||||||
</div>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
</div>
|
||||||
<label class="label" for="passwordValidation">{{ $t('user.auth.passwordRepeat') }}</label>
|
<div class="field">
|
||||||
<div class="control">
|
<label class="label" for="passwordValidation">{{ $t('user.auth.passwordRepeat') }}</label>
|
||||||
<input
|
<div class="control">
|
||||||
class="input"
|
<input
|
||||||
id="passwordValidation"
|
class="input"
|
||||||
name="passwordValidation"
|
id="passwordValidation"
|
||||||
:placeholder="$t('user.auth.passwordPlaceholder')"
|
name="passwordValidation"
|
||||||
required
|
:placeholder="$t('user.auth.passwordPlaceholder')"
|
||||||
type="password"
|
required
|
||||||
autocomplete="new-password"
|
type="password"
|
||||||
v-model="passwordValidation"
|
autocomplete="new-password"
|
||||||
@keyup.enter="submit"
|
v-model="passwordValidation"
|
||||||
/>
|
@keyup.enter="submit"
|
||||||
</div>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="field is-grouped">
|
<div class="field is-grouped">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<x-button
|
<x-button
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
id="register-submit"
|
id="register-submit"
|
||||||
@click="submit"
|
@click="submit"
|
||||||
class="mr-2"
|
class="mr-2"
|
||||||
>
|
>
|
||||||
{{ $t('user.auth.register') }}
|
{{ $t('user.auth.register') }}
|
||||||
</x-button>
|
</x-button>
|
||||||
<x-button :to="{ name: 'user.login' }" type="secondary">
|
<x-button :to="{ name: 'user.login' }" type="secondary">
|
||||||
{{ $t('user.auth.login') }}
|
{{ $t('user.auth.login') }}
|
||||||
</x-button>
|
</x-button>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<message v-if="loading">
|
</div>
|
||||||
{{ $t('misc.loading') }}
|
</form>
|
||||||
</message>
|
|
||||||
<message variant="danger" v-if="errorMessage !== ''">
|
|
||||||
{{ errorMessage }}
|
|
||||||
</message>
|
|
||||||
</form>
|
|
||||||
<legal/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -101,8 +94,6 @@ import {useI18n} from 'vue-i18n'
|
||||||
|
|
||||||
import router from '@/router'
|
import router from '@/router'
|
||||||
import {store} from '@/store'
|
import {store} from '@/store'
|
||||||
import {useTitle} from '@/composables/useTitle'
|
|
||||||
import Legal from '@/components/misc/legal'
|
|
||||||
import Message from '@/components/misc/message'
|
import Message from '@/components/misc/message'
|
||||||
|
|
||||||
// FIXME: use the `beforeEnter` hook of vue-router
|
// FIXME: use the `beforeEnter` hook of vue-router
|
||||||
|
@ -114,7 +105,6 @@ onBeforeMount(() => {
|
||||||
})
|
})
|
||||||
|
|
||||||
const {t} = useI18n()
|
const {t} = useI18n()
|
||||||
useTitle(() => t('user.auth.register'))
|
|
||||||
|
|
||||||
const credentials = reactive({
|
const credentials = reactive({
|
||||||
username: '',
|
username: '',
|
||||||
|
|
|
@ -1,67 +1,56 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<h2 class="title has-text-centered">{{ $t('user.auth.resetPassword') }}</h2>
|
<message variant="danger" v-if="errorMsg">
|
||||||
<div class="box">
|
{{ errorMsg }}
|
||||||
<form @submit.prevent="submit" v-if="!isSuccess">
|
</message>
|
||||||
<div class="field">
|
<div class="has-text-centered" v-if="isSuccess">
|
||||||
<label class="label" for="email">{{ $t('user.auth.email') }}</label>
|
<message variant="success">
|
||||||
<div class="control">
|
{{ $t('user.auth.resetPasswordSuccess') }}
|
||||||
<input
|
</message>
|
||||||
class="input"
|
<x-button :to="{ name: 'user.login' }">
|
||||||
id="email"
|
{{ $t('user.auth.login') }}
|
||||||
name="email"
|
</x-button>
|
||||||
:placeholder="$t('user.auth.emailPlaceholder')"
|
|
||||||
required
|
|
||||||
type="email"
|
|
||||||
v-focus
|
|
||||||
v-model="passwordReset.email"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="field is-grouped">
|
|
||||||
<div class="control">
|
|
||||||
<x-button
|
|
||||||
@click="submit"
|
|
||||||
:loading="passwordResetService.loading"
|
|
||||||
>
|
|
||||||
{{ $t('user.auth.resetPasswordAction') }}
|
|
||||||
</x-button>
|
|
||||||
<x-button :to="{ name: 'user.login' }" type="secondary">
|
|
||||||
{{ $t('user.auth.login') }}
|
|
||||||
</x-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<message variant="danger" v-if="errorMsg">
|
|
||||||
{{ errorMsg }}
|
|
||||||
</message>
|
|
||||||
</form>
|
|
||||||
<div class="has-text-centered" v-if="isSuccess">
|
|
||||||
<message variant="success">
|
|
||||||
{{ $t('user.auth.resetPasswordSuccess') }}
|
|
||||||
</message>
|
|
||||||
<x-button :to="{ name: 'user.login' }">
|
|
||||||
{{ $t('user.auth.login') }}
|
|
||||||
</x-button>
|
|
||||||
</div>
|
|
||||||
<Legal />
|
|
||||||
</div>
|
</div>
|
||||||
|
<form @submit.prevent="submit" v-if="!isSuccess">
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" for="email">{{ $t('user.auth.email') }}</label>
|
||||||
|
<div class="control">
|
||||||
|
<input
|
||||||
|
class="input"
|
||||||
|
id="email"
|
||||||
|
name="email"
|
||||||
|
:placeholder="$t('user.auth.emailPlaceholder')"
|
||||||
|
required
|
||||||
|
type="email"
|
||||||
|
v-focus
|
||||||
|
v-model="passwordReset.email"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field is-grouped">
|
||||||
|
<div class="control">
|
||||||
|
<x-button
|
||||||
|
@click="submit"
|
||||||
|
:loading="passwordResetService.loading"
|
||||||
|
>
|
||||||
|
{{ $t('user.auth.resetPasswordAction') }}
|
||||||
|
</x-button>
|
||||||
|
<x-button :to="{ name: 'user.login' }" type="secondary">
|
||||||
|
{{ $t('user.auth.login') }}
|
||||||
|
</x-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import {ref, reactive} from 'vue'
|
import {ref, reactive} from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
|
||||||
|
|
||||||
import Legal from '@/components/misc/legal'
|
|
||||||
|
|
||||||
import PasswordResetModel from '@/models/passwordReset'
|
import PasswordResetModel from '@/models/passwordReset'
|
||||||
import PasswordResetService from '@/services/passwordReset'
|
import PasswordResetService from '@/services/passwordReset'
|
||||||
import { useTitle } from '@/composables/useTitle'
|
|
||||||
import Message from '@/components/misc/message'
|
import Message from '@/components/misc/message'
|
||||||
|
|
||||||
const { t } = useI18n()
|
|
||||||
useTitle(() => t('user.auth.resetPassword'))
|
|
||||||
|
|
||||||
// Not sure if this instance needs a shalloRef at all
|
// Not sure if this instance needs a shalloRef at all
|
||||||
const passwordResetService = reactive(new PasswordResetService())
|
const passwordResetService = reactive(new PasswordResetService())
|
||||||
const passwordReset = ref(new PasswordResetModel())
|
const passwordReset = ref(new PasswordResetModel())
|
||||||
|
@ -73,7 +62,7 @@ async function submit() {
|
||||||
try {
|
try {
|
||||||
await passwordResetService.requestResetPassword(passwordReset.value)
|
await passwordResetService.requestResetPassword(passwordReset.value)
|
||||||
isSuccess.value = true
|
isSuccess.value = true
|
||||||
} catch(e) {
|
} catch (e) {
|
||||||
errorMsg.value = e.response.data.message
|
errorMsg.value = e.response.data.message
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,8 +131,10 @@ import {playPop} from '@/helpers/playPop'
|
||||||
import {useColorScheme} from '@/composables/useColorScheme'
|
import {useColorScheme} from '@/composables/useColorScheme'
|
||||||
import {success} from '@/message'
|
import {success} from '@/message'
|
||||||
|
|
||||||
|
const DEFAULT_LIST_ID = 0
|
||||||
|
|
||||||
function useColorSchemeSetting() {
|
function useColorSchemeSetting() {
|
||||||
const { t } = useI18n()
|
const {t} = useI18n()
|
||||||
const colorSchemeSettings = computed(() => ({
|
const colorSchemeSettings = computed(() => ({
|
||||||
light: t('user.settings.appearance.colorScheme.light'),
|
light: t('user.settings.appearance.colorScheme.light'),
|
||||||
auto: t('user.settings.appearance.colorScheme.system'),
|
auto: t('user.settings.appearance.colorScheme.system'),
|
||||||
|
@ -141,9 +143,11 @@ function useColorSchemeSetting() {
|
||||||
|
|
||||||
const {store} = useColorScheme()
|
const {store} = useColorScheme()
|
||||||
watch(store, (schemeId) => {
|
watch(store, (schemeId) => {
|
||||||
success({message: t('user.settings.appearance.setSuccess', {
|
success({
|
||||||
colorScheme: colorSchemeSettings.value[schemeId],
|
message: t('user.settings.appearance.setSuccess', {
|
||||||
})})
|
colorScheme: colorSchemeSettings.value[schemeId],
|
||||||
|
}),
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -178,8 +182,13 @@ export default {
|
||||||
.map(l => ({code: l[0], title: l[1]}))
|
.map(l => ({code: l[0], title: l[1]}))
|
||||||
.sort((a, b) => a.title.localeCompare(b.title))
|
.sort((a, b) => a.title.localeCompare(b.title))
|
||||||
},
|
},
|
||||||
defaultList() {
|
defaultList: {
|
||||||
return this.$store.getters['lists/getListById'](this.settings.defaultListId)
|
get() {
|
||||||
|
return this.$store.getters['lists/getListById'](this.settings.defaultListId)
|
||||||
|
},
|
||||||
|
set(l) {
|
||||||
|
this.settings.defaultListId = l ? l.id : DEFAULT_LIST_ID
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -204,12 +213,13 @@ export default {
|
||||||
localStorage.setItem(playSoundWhenDoneKey, this.playSoundWhenDone)
|
localStorage.setItem(playSoundWhenDoneKey, this.playSoundWhenDone)
|
||||||
saveLanguage(this.language)
|
saveLanguage(this.language)
|
||||||
setQuickAddMagicMode(this.quickAddMagicMode)
|
setQuickAddMagicMode(this.quickAddMagicMode)
|
||||||
this.settings.defaultListId = this.defaultList ? this.defaultList.id : 0
|
|
||||||
|
|
||||||
await this.userSettingsService.update(this.settings)
|
const settings = {
|
||||||
this.$store.commit('auth/setUserSettings', {
|
|
||||||
...this.settings,
|
...this.settings,
|
||||||
})
|
}
|
||||||
|
|
||||||
|
await this.userSettingsService.update(settings)
|
||||||
|
this.$store.commit('auth/setUserSettings', settings)
|
||||||
this.$message.success({message: this.$t('user.settings.general.savedSuccess')})
|
this.$message.success({message: this.$t('user.settings.general.savedSuccess')})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
Reference in New Issue