feat: add message component (#1082)
This PR adds a simple message component that replaces bulma's default message. Co-authored-by: kolaente <k@knt.li> Reviewed-on: https://kolaente.dev/vikunja/frontend/pulls/1082 Reviewed-by: dpschen <dpschen@noreply.kolaente.de> Co-authored-by: konrad <k@knt.li> Co-committed-by: konrad <k@knt.li>
This commit is contained in:
parent
59e915cc10
commit
f8d009a6aa
23 changed files with 170 additions and 127 deletions
|
@ -8,7 +8,7 @@ const testAndAssertFailed = fixture => {
|
||||||
|
|
||||||
cy.wait(5000) // It can take waaaayy too long to log the user in
|
cy.wait(5000) // It can take waaaayy too long to log the user in
|
||||||
cy.url().should('include', '/')
|
cy.url().should('include', '/')
|
||||||
cy.get('div.notification.is-danger').contains('Wrong username or password.')
|
cy.get('div.message.danger').contains('Wrong username or password.')
|
||||||
}
|
}
|
||||||
|
|
||||||
context('Login', () => {
|
context('Login', () => {
|
||||||
|
|
|
@ -32,7 +32,7 @@ context('Registration', () => {
|
||||||
cy.get('h2').should('contain', `Hi ${fixture.username}!`)
|
cy.get('h2').should('contain', `Hi ${fixture.username}!`)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should fail', () => {
|
it.only('Should fail', () => {
|
||||||
const fixture = {
|
const fixture = {
|
||||||
username: 'test',
|
username: 'test',
|
||||||
password: '123456',
|
password: '123456',
|
||||||
|
@ -45,6 +45,6 @@ context('Registration', () => {
|
||||||
cy.get('#password').type(fixture.password)
|
cy.get('#password').type(fixture.password)
|
||||||
cy.get('#passwordValidation').type(fixture.password)
|
cy.get('#passwordValidation').type(fixture.password)
|
||||||
cy.get('#register-submit').click()
|
cy.get('#register-submit').click()
|
||||||
cy.get('div.notification.is-danger').contains('A user with this username already exists.')
|
cy.get('div.message.danger').contains('A user with this username already exists.')
|
||||||
})
|
})
|
||||||
})
|
})
|
|
@ -130,7 +130,10 @@
|
||||||
"ignorePatterns": [
|
"ignorePatterns": [
|
||||||
"*.test.*",
|
"*.test.*",
|
||||||
"cypress/*"
|
"cypress/*"
|
||||||
]
|
],
|
||||||
|
"globals": {
|
||||||
|
"defineProps": "readonly"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"postcss": {
|
"postcss": {
|
||||||
"plugins": {
|
"plugins": {
|
||||||
|
|
|
@ -30,27 +30,25 @@
|
||||||
<a @click="() => (configureApi = true)">{{ $t('apiConfig.change') }}</a>
|
<a @click="() => (configureApi = true)">{{ $t('apiConfig.change') }}</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<message variant="success" v-if="successMsg !== '' && errorMsg === ''" class="mt-2">
|
||||||
class="notification is-success mt-2"
|
|
||||||
v-if="successMsg !== '' && errorMsg === ''"
|
|
||||||
>
|
|
||||||
{{ successMsg }}
|
{{ successMsg }}
|
||||||
</div>
|
</message>
|
||||||
<div
|
<message variant="danger" v-if="errorMsg !== '' && successMsg === ''" class="mt-2">
|
||||||
class="notification is-danger mt-2"
|
|
||||||
v-if="errorMsg !== '' && successMsg === ''"
|
|
||||||
>
|
|
||||||
{{ errorMsg }}
|
{{ errorMsg }}
|
||||||
</div>
|
</message>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import Message from '@/components/misc/message'
|
||||||
import {parseURL} from 'ufo'
|
import {parseURL} from 'ufo'
|
||||||
import {checkAndSetApiUrl} from '@/helpers/checkAndSetApiUrl'
|
import {checkAndSetApiUrl} from '@/helpers/checkAndSetApiUrl'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'apiConfig',
|
name: 'apiConfig',
|
||||||
|
components: {
|
||||||
|
Message,
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
configureApi: false,
|
configureApi: false,
|
||||||
|
|
|
@ -1,15 +1,18 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="notification is-danger">
|
<message variant="danger">
|
||||||
<i18n-t keypath="loadingError.failed">
|
<i18n-t keypath="loadingError.failed">
|
||||||
<a @click="reload">{{ $t('loadingError.tryAgain') }}</a>
|
<a @click="reload">{{ $t('loadingError.tryAgain') }}</a>
|
||||||
<a href="https://vikunja.io/contact/" rel="noreferrer noopener nofollow" target="_blank">{{ $t('loadingError.contact') }}</a>
|
<a href="https://vikunja.io/contact/" rel="noreferrer noopener nofollow" target="_blank">{{ $t('loadingError.contact') }}</a>
|
||||||
</i18n-t>
|
</i18n-t>
|
||||||
</div>
|
</message>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import Message from '@/components/misc/message'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'error',
|
name: 'error',
|
||||||
|
components: {Message},
|
||||||
methods: {
|
methods: {
|
||||||
reload() {
|
reload() {
|
||||||
window.location.reload()
|
window.location.reload()
|
||||||
|
|
|
@ -4,13 +4,11 @@
|
||||||
<template v-for="(s, i) in shortcuts" :key="i">
|
<template v-for="(s, i) in shortcuts" :key="i">
|
||||||
<h3>{{ $t(s.title) }}</h3>
|
<h3>{{ $t(s.title) }}</h3>
|
||||||
|
|
||||||
<div class="message is-primary">
|
<message>
|
||||||
<div class="message-body">
|
{{
|
||||||
{{
|
s.available($route) ? $t('keyboardShortcuts.currentPageOnly') : $t('keyboardShortcuts.allPages')
|
||||||
s.available($route) ? $t('keyboardShortcuts.currentPageOnly') : $t('keyboardShortcuts.allPages')
|
}}
|
||||||
}}
|
</message>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<dl class="shortcut-list">
|
<dl class="shortcut-list">
|
||||||
<template v-for="(sc, si) in s.shortcuts" :key="si">
|
<template v-for="(sc, si) in s.shortcuts" :key="si">
|
||||||
|
@ -30,11 +28,15 @@
|
||||||
<script>
|
<script>
|
||||||
import {KEYBOARD_SHORTCUTS_ACTIVE} from '@/store/mutation-types'
|
import {KEYBOARD_SHORTCUTS_ACTIVE} from '@/store/mutation-types'
|
||||||
import Shortcut from '@/components/misc/shortcut.vue'
|
import Shortcut from '@/components/misc/shortcut.vue'
|
||||||
|
import Message from '@/components/misc/message'
|
||||||
import {KEYBOARD_SHORTCUTS} from './shortcuts'
|
import {KEYBOARD_SHORTCUTS} from './shortcuts'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'keyboard-shortcuts',
|
name: 'keyboard-shortcuts',
|
||||||
components: {Shortcut},
|
components: {
|
||||||
|
Message,
|
||||||
|
Shortcut,
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
shortcuts: KEYBOARD_SHORTCUTS,
|
shortcuts: KEYBOARD_SHORTCUTS,
|
||||||
|
|
41
src/components/misc/message.vue
Normal file
41
src/components/misc/message.vue
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
<template>
|
||||||
|
<div class="message" :class="variant">
|
||||||
|
<slot/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
defineProps({
|
||||||
|
variant: {
|
||||||
|
type: String,
|
||||||
|
default: 'info',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.message {
|
||||||
|
padding: .75rem 1rem;
|
||||||
|
border-radius: $radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
border: 1px solid var(--primary);
|
||||||
|
background: hsla(var(--primary-hsl), .05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.danger {
|
||||||
|
border: 1px solid var(--danger);
|
||||||
|
background: hsla(var(--danger-h), var(--danger-s), var(--danger-l), .05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning {
|
||||||
|
border: 1px solid var(--warning);
|
||||||
|
background: hsla(var(--warning-h), var(--warning-s), var(--warning-l), .05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.success {
|
||||||
|
border: 1px solid var(--success);
|
||||||
|
background: hsla(var(--success-h), var(--success-s), var(--success-l), .05);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -2,14 +2,9 @@
|
||||||
<div class="no-auth-wrapper">
|
<div class="no-auth-wrapper">
|
||||||
<div class="noauth-container">
|
<div class="noauth-container">
|
||||||
<Logo class="logo" width="400" height="117" />
|
<Logo class="logo" width="400" height="117" />
|
||||||
<div class="message is-info" v-if="motd !== ''">
|
<message v-if="motd !== ''" class="my-2">
|
||||||
<div class="message-header">
|
{{ motd }}
|
||||||
<p>{{ $t('misc.info') }}</p>
|
</message>
|
||||||
</div>
|
|
||||||
<div class="message-body">
|
|
||||||
{{ motd }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<slot/>
|
<slot/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -17,6 +12,7 @@
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import Logo from '@/components/home/Logo.vue'
|
import Logo from '@/components/home/Logo.vue'
|
||||||
|
import message from '@/components/misc/message'
|
||||||
import {useStore} from 'vuex'
|
import {useStore} from 'vuex'
|
||||||
import {computed} from 'vue'
|
import {computed} from 'vue'
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
<p v-if="error === errorNoApiUrl">
|
<p v-if="error === errorNoApiUrl">
|
||||||
{{ $t('ready.noApiUrlConfigured') }}
|
{{ $t('ready.noApiUrlConfigured') }}
|
||||||
</p>
|
</p>
|
||||||
<div class="notification is-danger" v-else>
|
<message variant="danger" v-else>
|
||||||
<p>
|
<p>
|
||||||
{{ $t('ready.errorOccured') }}<br/>
|
{{ $t('ready.errorOccured') }}<br/>
|
||||||
{{ error }}
|
{{ error }}
|
||||||
|
@ -24,7 +24,7 @@
|
||||||
<p>
|
<p>
|
||||||
{{ $t('ready.checkApiUrl') }}
|
{{ $t('ready.checkApiUrl') }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</message>
|
||||||
<api-config :configure-open="true" @found-api="load"/>
|
<api-config :configure-open="true" @found-api="load"/>
|
||||||
</card>
|
</card>
|
||||||
</no-auth-wrapper>
|
</no-auth-wrapper>
|
||||||
|
@ -43,6 +43,7 @@
|
||||||
<script>
|
<script>
|
||||||
import Logo from '@/assets/logo.svg?component'
|
import Logo from '@/assets/logo.svg?component'
|
||||||
import ApiConfig from '@/components/misc/api-config'
|
import ApiConfig from '@/components/misc/api-config'
|
||||||
|
import Message from '@/components/misc/message'
|
||||||
import NoAuthWrapper from '@/components/misc/no-auth-wrapper'
|
import NoAuthWrapper from '@/components/misc/no-auth-wrapper'
|
||||||
import {mapState} from 'vuex'
|
import {mapState} from 'vuex'
|
||||||
import {ERROR_NO_API_URL} from '@/helpers/checkAndSetApiUrl'
|
import {ERROR_NO_API_URL} from '@/helpers/checkAndSetApiUrl'
|
||||||
|
@ -50,6 +51,7 @@ import {ERROR_NO_API_URL} from '@/helpers/checkAndSetApiUrl'
|
||||||
export default {
|
export default {
|
||||||
name: 'ready',
|
name: 'ready',
|
||||||
components: {
|
components: {
|
||||||
|
Message,
|
||||||
Logo,
|
Logo,
|
||||||
NoAuthWrapper,
|
NoAuthWrapper,
|
||||||
ApiConfig,
|
ApiConfig,
|
||||||
|
|
|
@ -193,10 +193,6 @@ export default {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-body {
|
|
||||||
padding: .5rem .75rem;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
@import "bulma-css-variables/sass/elements/content";
|
@import "bulma-css-variables/sass/elements/content";
|
||||||
@import "bulma-css-variables/sass/elements/icon";
|
@import "bulma-css-variables/sass/elements/icon";
|
||||||
@import "bulma-css-variables/sass/elements/image";
|
@import "bulma-css-variables/sass/elements/image";
|
||||||
@import "bulma-css-variables/sass/elements/notification";
|
//@import "bulma-css-variables/sass/elements/notification"; // not used
|
||||||
@import "bulma-css-variables/sass/elements/progress";
|
@import "bulma-css-variables/sass/elements/progress";
|
||||||
@import "bulma-css-variables/sass/elements/table";
|
@import "bulma-css-variables/sass/elements/table";
|
||||||
@import "bulma-css-variables/sass/elements/tag";
|
@import "bulma-css-variables/sass/elements/tag";
|
||||||
|
@ -48,7 +48,7 @@
|
||||||
// @import "bulma-css-variables/sass/components/level"; // not used
|
// @import "bulma-css-variables/sass/components/level"; // not used
|
||||||
@import "bulma-css-variables/sass/components/media";
|
@import "bulma-css-variables/sass/components/media";
|
||||||
@import "bulma-css-variables/sass/components/menu";
|
@import "bulma-css-variables/sass/components/menu";
|
||||||
@import "bulma-css-variables/sass/components/message";
|
//@import "bulma-css-variables/sass/components/message"; // not used
|
||||||
@import "bulma-css-variables/sass/components/modal";
|
@import "bulma-css-variables/sass/components/modal";
|
||||||
@import "bulma-css-variables/sass/components/navbar";
|
@import "bulma-css-variables/sass/components/navbar";
|
||||||
@import "bulma-css-variables/sass/components/pagination";
|
@import "bulma-css-variables/sass/components/pagination";
|
||||||
|
|
|
@ -7,5 +7,4 @@
|
||||||
@import "form";
|
@import "form";
|
||||||
@import "link-share";
|
@import "link-share";
|
||||||
@import "loading";
|
@import "loading";
|
||||||
@import "notification";
|
|
||||||
@import "flatpickr";
|
@import "flatpickr";
|
|
@ -1,12 +0,0 @@
|
||||||
.notification {
|
|
||||||
border: $thickness solid $border;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notifications {
|
|
||||||
left: 0.5rem !important;
|
|
||||||
bottom: 1rem !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message .message-body {
|
|
||||||
border: $thickness solid;
|
|
||||||
}
|
|
|
@ -3,7 +3,7 @@
|
||||||
<h2 v-if="userInfo">
|
<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>
|
||||||
<div class="notification is-danger" v-if="deletionScheduledAt !== null">
|
<message variant="danger" v-if="deletionScheduledAt !== null">
|
||||||
{{
|
{{
|
||||||
$t('user.deletion.scheduled', {
|
$t('user.deletion.scheduled', {
|
||||||
date: formatDateShort(deletionScheduledAt),
|
date: formatDateShort(deletionScheduledAt),
|
||||||
|
@ -13,7 +13,7 @@
|
||||||
<router-link :to="{name: 'user.settings', hash: '#deletion'}">
|
<router-link :to="{name: 'user.settings', hash: '#deletion'}">
|
||||||
{{ $t('user.deletion.scheduledCancel') }}
|
{{ $t('user.deletion.scheduledCancel') }}
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</message>
|
||||||
<add-task
|
<add-task
|
||||||
:listId="defaultListId"
|
:listId="defaultListId"
|
||||||
@taskAdded="updateTaskList"
|
@taskAdded="updateTaskList"
|
||||||
|
@ -57,6 +57,7 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {mapState} from 'vuex'
|
import {mapState} from 'vuex'
|
||||||
|
import Message from '@/components/misc/message'
|
||||||
import ShowTasks from './tasks/ShowTasks.vue'
|
import ShowTasks from './tasks/ShowTasks.vue'
|
||||||
import {getHistory} from '../modules/listHistory'
|
import {getHistory} from '../modules/listHistory'
|
||||||
import ListCard from '@/components/list/partials/list-card.vue'
|
import ListCard from '@/components/list/partials/list-card.vue'
|
||||||
|
@ -67,6 +68,7 @@ import {parseDateOrNull} from '../helpers/parseDateOrNull'
|
||||||
export default {
|
export default {
|
||||||
name: 'Home',
|
name: 'Home',
|
||||||
components: {
|
components: {
|
||||||
|
Message,
|
||||||
ListCard,
|
ListCard,
|
||||||
ShowTasks,
|
ShowTasks,
|
||||||
AddTask,
|
AddTask,
|
||||||
|
@ -147,7 +149,7 @@ export default {
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
max-height: calc(#{$list-height * 2} + #{$list-spacing * 2} - 4px);
|
max-height: calc(#{$list-height * 2} + #{$list-spacing * 2} - 4px);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
@media screen and (max-width: $mobile) {
|
@media screen and (max-width: $mobile) {
|
||||||
max-height: calc(#{$list-height * 4} + #{$list-spacing * 4} - 4px);
|
max-height: calc(#{$list-height * 4} + #{$list-spacing * 4} - 4px);
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,9 +36,9 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<transition name="fade">
|
<transition name="fade">
|
||||||
<div class="notification is-warning" v-if="currentList.isArchived">
|
<message variant="warning" v-if="currentList.isArchived" class="mb-4">
|
||||||
{{ $t('list.archived') }}
|
{{ $t('list.archived') }}
|
||||||
</div>
|
</message>
|
||||||
</transition>
|
</transition>
|
||||||
|
|
||||||
<router-view/>
|
<router-view/>
|
||||||
|
@ -46,6 +46,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import Message from '@/components/misc/message'
|
||||||
import ListModel from '../../models/list'
|
import ListModel from '../../models/list'
|
||||||
import ListService from '../../services/list'
|
import ListService from '../../services/list'
|
||||||
import {CURRENT_LIST} from '../../store/mutation-types'
|
import {CURRENT_LIST} from '../../store/mutation-types'
|
||||||
|
@ -53,6 +54,7 @@ import {getListView} from '../../helpers/saveListView'
|
||||||
import {saveListToHistory} from '../../modules/listHistory'
|
import {saveListToHistory} from '../../modules/listHistory'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
components: {Message},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
listService: new ListService(),
|
listService: new ListService(),
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
<div class="migration-in-progress">
|
<div class="migration-in-progress">
|
||||||
<img :alt="migrator.name" :src="migrator.icon" class="logo"/>
|
<img :alt="migrator.name" :src="migrator.icon" class="logo"/>
|
||||||
<div class="progress-dots">
|
<div class="progress-dots">
|
||||||
<span v-for="i in progressDotsCount" :key="i" />
|
<span v-for="i in progressDotsCount" :key="i"/>
|
||||||
</div>
|
</div>
|
||||||
<Logo class="logo"/>
|
<Logo class="logo"/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -57,11 +57,9 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<div class="message is-primary">
|
<message class="mb-4">
|
||||||
<div class="message-body">
|
{{ message }}
|
||||||
{{ message }}
|
</message>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<x-button :to="{name: 'home'}">{{ $t('misc.refresh') }}</x-button>
|
<x-button :to="{name: 'home'}">{{ $t('misc.refresh') }}</x-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -71,6 +69,7 @@
|
||||||
import AbstractMigrationService from '@/services/migrator/abstractMigration'
|
import AbstractMigrationService from '@/services/migrator/abstractMigration'
|
||||||
import AbstractMigrationFileService from '@/services/migrator/abstractMigrationFile'
|
import AbstractMigrationFileService from '@/services/migrator/abstractMigrationFile'
|
||||||
import Logo from '@/assets/logo.svg?component'
|
import Logo from '@/assets/logo.svg?component'
|
||||||
|
import Message from '@/components/misc/message'
|
||||||
|
|
||||||
import {MIGRATORS} from './migrators'
|
import {MIGRATORS} from './migrators'
|
||||||
|
|
||||||
|
@ -79,7 +78,10 @@ const PROGRESS_DOTS_COUNT = 8
|
||||||
export default {
|
export default {
|
||||||
name: 'MigrateService',
|
name: 'MigrateService',
|
||||||
|
|
||||||
components: { Logo },
|
components: {
|
||||||
|
Logo,
|
||||||
|
Message,
|
||||||
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -101,7 +103,7 @@ export default {
|
||||||
|
|
||||||
beforeRouteEnter(to) {
|
beforeRouteEnter(to) {
|
||||||
if (MIGRATORS[to.params.service] === undefined) {
|
if (MIGRATORS[to.params.service] === undefined) {
|
||||||
return { name: 'not-found' }
|
return {name: 'not-found'}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -122,7 +124,7 @@ export default {
|
||||||
if (this.migrator.isFileMigrator) {
|
if (this.migrator.isFileMigrator) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.authUrl = await this.migrationService.getAuthUrl().then(({url}) => url)
|
this.authUrl = await this.migrationService.getAuthUrl().then(({url}) => url)
|
||||||
|
|
||||||
this.migratorAuthCode = location.hash.startsWith('#token=')
|
this.migratorAuthCode = location.hash.startsWith('#token=')
|
||||||
|
@ -150,7 +152,7 @@ export default {
|
||||||
this.lastMigrationDate = null
|
this.lastMigrationDate = null
|
||||||
this.message = ''
|
this.message = ''
|
||||||
|
|
||||||
let migrationConfig = { code: this.migratorAuthCode }
|
let migrationConfig = {code: this.migratorAuthCode}
|
||||||
|
|
||||||
if (this.migrator.isFileMigrator) {
|
if (this.migrator.isFileMigrator) {
|
||||||
if (this.$refs.uploadInput.files.length === 0) {
|
if (this.$refs.uploadInput.files.length === 0) {
|
||||||
|
@ -160,7 +162,7 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { message } = await this.migrationService.migrate(migrationConfig)
|
const {message} = await this.migrationService.migrate(migrationConfig)
|
||||||
this.message = message
|
this.message = message
|
||||||
return this.$store.dispatch('namespaces/loadNamespaces')
|
return this.$store.dispatch('namespaces/loadNamespaces')
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -173,24 +175,24 @@ export default {
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.migration-in-progress-container {
|
.migration-in-progress-container {
|
||||||
max-width: 400px;
|
max-width: 400px;
|
||||||
margin: 4rem auto 0;
|
margin: 4rem auto 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.migration-in-progress {
|
.migration-in-progress {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
max-width: 400px;
|
max-width: 400px;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 2rem;
|
margin-bottom: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo {
|
.logo {
|
||||||
display: block;
|
display: block;
|
||||||
max-height: 100px;
|
max-height: 100px;
|
||||||
max-width: 100px;
|
max-width: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress-dots {
|
.progress-dots {
|
||||||
|
|
|
@ -28,19 +28,20 @@
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label">{{ $t('namespace.attributes.color') }}</label>
|
<label class="label">{{ $t('namespace.attributes.color') }}</label>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<color-picker v-model="namespace.hexColor" />
|
<color-picker v-model="namespace.hexColor"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="notification is-info mt-4">
|
<message class="mt-4">
|
||||||
<h4 class="title">{{ $t('namespace.create.tooltip') }}</h4>
|
<h4 class="title">{{ $t('namespace.create.tooltip') }}</h4>
|
||||||
|
|
||||||
{{ $t('namespace.create.explanation') }}
|
{{ $t('namespace.create.explanation') }}
|
||||||
</div>
|
</message>
|
||||||
</create-edit>
|
</create-edit>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import Message from '@/components/misc/message'
|
||||||
import NamespaceModel from '../../models/namespace'
|
import NamespaceModel from '../../models/namespace'
|
||||||
import NamespaceService from '../../services/namespace'
|
import NamespaceService from '../../services/namespace'
|
||||||
import CreateEdit from '@/components/misc/create-edit.vue'
|
import CreateEdit from '@/components/misc/create-edit.vue'
|
||||||
|
@ -56,6 +57,7 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
|
Message,
|
||||||
ColorPicker,
|
ColorPicker,
|
||||||
CreateEdit,
|
CreateEdit,
|
||||||
},
|
},
|
||||||
|
@ -72,7 +74,7 @@ export default {
|
||||||
|
|
||||||
const namespace = await this.namespaceService.create(this.namespace)
|
const namespace = await this.namespaceService.create(this.namespace)
|
||||||
this.$store.commit('namespaces/addNamespace', namespace)
|
this.$store.commit('namespaces/addNamespace', namespace)
|
||||||
this.$message.success({message: this.$t('namespace.create.success') })
|
this.$message.success({message: this.$t('namespace.create.success')})
|
||||||
this.$router.back()
|
this.$router.back()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div class="notification is-info is-light has-text-centered" v-if="loading">
|
<message v-if="loading">
|
||||||
{{ $t('sharing.authenticating') }}
|
{{ $t('sharing.authenticating') }}
|
||||||
</div>
|
</message>
|
||||||
<div v-if="authenticateWithPassword" class="box">
|
<div v-if="authenticateWithPassword" class="box">
|
||||||
<p class="pb-2">
|
<p class="pb-2">
|
||||||
{{ $t('sharing.passwordRequired') }}
|
{{ $t('sharing.passwordRequired') }}
|
||||||
|
@ -25,18 +25,20 @@
|
||||||
{{ $t('user.auth.login') }}
|
{{ $t('user.auth.login') }}
|
||||||
</x-button>
|
</x-button>
|
||||||
|
|
||||||
<div class="notification is-danger mt-4" v-if="errorMessage !== ''">
|
<message variant="danger" class="mt-4" v-if="errorMessage !== ''">
|
||||||
{{ errorMessage }}
|
{{ errorMessage }}
|
||||||
</div>
|
</message>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {mapGetters} from 'vuex'
|
import {mapGetters} from 'vuex'
|
||||||
|
import Message from '@/components/misc/message'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'LinkSharingAuth',
|
name: 'LinkSharingAuth',
|
||||||
|
components: {Message},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loading: true,
|
loading: true,
|
||||||
|
@ -72,7 +74,7 @@ export default {
|
||||||
password: this.password,
|
password: this.password,
|
||||||
})
|
})
|
||||||
this.$router.push({name: 'list.list', params: {listId: r.list_id}})
|
this.$router.push({name: 'list.list', params: {listId: r.list_id}})
|
||||||
} catch(e) {
|
} catch (e) {
|
||||||
if (typeof e.response.data.code !== 'undefined' && e.response.data.code === 13001) {
|
if (typeof e.response.data.code !== 'undefined' && e.response.data.code === 13001) {
|
||||||
this.authenticateWithPassword = true
|
this.authenticateWithPassword = true
|
||||||
return
|
return
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
<div>
|
<div>
|
||||||
<h2 class="title has-text-centered">Login</h2>
|
<h2 class="title has-text-centered">Login</h2>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="notification is-success has-text-centered" v-if="confirmedEmailSuccess">
|
<message variant="success" class="has-text-centered" v-if="confirmedEmailSuccess">
|
||||||
{{ $t('user.auth.confirmEmailSuccess') }}
|
{{ $t('user.auth.confirmEmailSuccess') }}
|
||||||
</div>
|
</message>
|
||||||
<api-config @foundApi="hasApiUrl = true"/>
|
<api-config @foundApi="hasApiUrl = true"/>
|
||||||
<form @submit.prevent="submit" id="loginform" v-if="hasApiUrl && localAuthEnabled">
|
<form @submit.prevent="submit" id="loginform" v-if="hasApiUrl && localAuthEnabled">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
|
@ -78,9 +78,9 @@
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="notification is-danger" v-if="errorMessage">
|
<message variant="danger" v-if="errorMessage">
|
||||||
{{ errorMessage }}
|
{{ errorMessage }}
|
||||||
</div>
|
</message>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
@ -110,11 +110,13 @@ import {LOADING} from '@/store/mutation-types'
|
||||||
import legal from '../../components/misc/legal'
|
import legal from '../../components/misc/legal'
|
||||||
import ApiConfig from '@/components/misc/api-config.vue'
|
import ApiConfig from '@/components/misc/api-config.vue'
|
||||||
import {getErrorText} from '@/message'
|
import {getErrorText} from '@/message'
|
||||||
|
import Message from '@/components/misc/message'
|
||||||
import {redirectToProvider} from '../../helpers/redirectToProvider'
|
import {redirectToProvider} from '../../helpers/redirectToProvider'
|
||||||
import {getLastVisited, clearLastVisited} from '../../helpers/saveLastVisited'
|
import {getLastVisited, clearLastVisited} from '../../helpers/saveLastVisited'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
Message,
|
||||||
ApiConfig,
|
ApiConfig,
|
||||||
legal,
|
legal,
|
||||||
},
|
},
|
||||||
|
@ -149,7 +151,7 @@ export default {
|
||||||
const last = getLastVisited()
|
const last = getLastVisited()
|
||||||
if (last !== null) {
|
if (last !== null) {
|
||||||
this.$router.push({
|
this.$router.push({
|
||||||
name: last.name,
|
name: last.name,
|
||||||
params: last.params,
|
params: last.params,
|
||||||
})
|
})
|
||||||
clearLastVisited()
|
clearLastVisited()
|
||||||
|
@ -206,7 +208,7 @@ export default {
|
||||||
try {
|
try {
|
||||||
await this.$store.dispatch('auth/login', credentials)
|
await this.$store.dispatch('auth/login', credentials)
|
||||||
this.$store.commit('auth/needsTotpPasscode', false)
|
this.$store.commit('auth/needsTotpPasscode', false)
|
||||||
} catch(e) {
|
} catch (e) {
|
||||||
if (e.response && e.response.data.code === 1017 && !credentials.totpPasscode) {
|
if (e.response && e.response.data.code === 1017 && !credentials.totpPasscode) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div class="notification is-danger" v-if="errorMessage">
|
<message variant="danger" v-if="errorMessage">
|
||||||
{{ errorMessage }}
|
{{ errorMessage }}
|
||||||
</div>
|
</message>
|
||||||
<div class="notification is-info" v-if="loading">
|
<message v-if="loading">
|
||||||
{{ $t('user.auth.authenticating') }}
|
{{ $t('user.auth.authenticating') }}
|
||||||
</div>
|
</message>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -14,10 +14,12 @@ import {mapState} from 'vuex'
|
||||||
|
|
||||||
import {LOADING} from '@/store/mutation-types'
|
import {LOADING} from '@/store/mutation-types'
|
||||||
import {getErrorText} from '@/message'
|
import {getErrorText} from '@/message'
|
||||||
|
import Message from '@/components/misc/message'
|
||||||
import {clearLastVisited, getLastVisited} from '../../helpers/saveLastVisited'
|
import {clearLastVisited, getLastVisited} from '../../helpers/saveLastVisited'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Auth',
|
name: 'Auth',
|
||||||
|
components: {Message},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
errorMessage: '',
|
errorMessage: '',
|
||||||
|
|
|
@ -45,37 +45,37 @@
|
||||||
</x-button>
|
</x-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="notification is-info" v-if="this.passwordResetService.loading">
|
<message v-if="this.passwordResetService.loading">
|
||||||
{{ $t('misc.loading') }}
|
{{ $t('misc.loading') }}
|
||||||
</div>
|
</message>
|
||||||
<div class="notification is-danger" v-if="errorMsg">
|
<message v-if="errorMsg">
|
||||||
{{ errorMsg }}
|
{{ errorMsg }}
|
||||||
</div>
|
</message>
|
||||||
</form>
|
</form>
|
||||||
<div class="has-text-centered" v-if="successMessage">
|
<div class="has-text-centered" v-if="successMessage">
|
||||||
<div class="notification is-success">
|
<message variant="success">
|
||||||
{{ successMessage }}
|
{{ successMessage }}
|
||||||
</div>
|
</message>
|
||||||
<x-button :to="{ name: 'user.login' }">
|
<x-button :to="{ name: 'user.login' }">
|
||||||
{{ $t('user.auth.login') }}
|
{{ $t('user.auth.login') }}
|
||||||
</x-button>
|
</x-button>
|
||||||
</div>
|
</div>
|
||||||
<Legal />
|
<Legal/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
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 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 {useTitle} from '@/composables/useTitle'
|
||||||
|
import Message from '@/components/misc/message'
|
||||||
|
|
||||||
const { t } = useI18n()
|
const {t} = useI18n()
|
||||||
useTitle(() => t('user.auth.resetPassword'))
|
useTitle(() => t('user.auth.resetPassword'))
|
||||||
|
|
||||||
const credentials = reactive({
|
const credentials = reactive({
|
||||||
|
@ -97,10 +97,10 @@ async function submit() {
|
||||||
|
|
||||||
const passwordReset = new PasswordResetModel({newPassword: credentials.password})
|
const passwordReset = new PasswordResetModel({newPassword: credentials.password})
|
||||||
try {
|
try {
|
||||||
const { message } = passwordResetService.resetPassword(passwordReset)
|
const {message} = passwordResetService.resetPassword(passwordReset)
|
||||||
successMessage.value = message
|
successMessage.value = message
|
||||||
localStorage.removeItem('passwordResetToken')
|
localStorage.removeItem('passwordResetToken')
|
||||||
} catch(e) {
|
} catch (e) {
|
||||||
errorMsg.value = e.response.data.message
|
errorMsg.value = e.response.data.message
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,12 +83,12 @@
|
||||||
</x-button>
|
</x-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="notification is-info" v-if="loading">
|
<message v-if="loading">
|
||||||
{{ $t('misc.loading') }}
|
{{ $t('misc.loading') }}
|
||||||
</div>
|
</message>
|
||||||
<div class="notification is-danger" v-if="errorMessage !== ''">
|
<message variant="danger" v-if="errorMessage !== ''">
|
||||||
{{ errorMessage }}
|
{{ errorMessage }}
|
||||||
</div>
|
</message>
|
||||||
</form>
|
</form>
|
||||||
<legal/>
|
<legal/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -97,13 +97,13 @@
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import {ref, reactive, toRaw, computed, onBeforeMount} from 'vue'
|
import {ref, reactive, toRaw, computed, onBeforeMount} from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
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 {useTitle} from '@/composables/useTitle'
|
||||||
|
|
||||||
import Legal from '@/components/misc/legal'
|
import Legal from '@/components/misc/legal'
|
||||||
|
import Message from '@/components/misc/message'
|
||||||
|
|
||||||
// FIXME: use the `beforeEnter` hook of vue-router
|
// FIXME: use the `beforeEnter` hook of vue-router
|
||||||
// Check if the user is already logged in, if so, redirect them to the homepage
|
// Check if the user is already logged in, if so, redirect them to the homepage
|
||||||
|
@ -113,7 +113,7 @@ onBeforeMount(() => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const { t } = useI18n()
|
const {t} = useI18n()
|
||||||
useTitle(() => t('user.auth.register'))
|
useTitle(() => t('user.auth.register'))
|
||||||
|
|
||||||
const credentials = reactive({
|
const credentials = reactive({
|
||||||
|
@ -137,7 +137,7 @@ async function submit() {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await store.dispatch('auth/register', toRaw(credentials))
|
await store.dispatch('auth/register', toRaw(credentials))
|
||||||
} catch(e) {
|
} catch (e) {
|
||||||
errorMessage.value = e.message
|
errorMessage.value = e.message
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,14 +31,14 @@
|
||||||
</x-button>
|
</x-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="notification is-danger" v-if="errorMsg">
|
<message variant="danger" v-if="errorMsg">
|
||||||
{{ errorMsg }}
|
{{ errorMsg }}
|
||||||
</div>
|
</message>
|
||||||
</form>
|
</form>
|
||||||
<div class="has-text-centered" v-if="isSuccess">
|
<div class="has-text-centered" v-if="isSuccess">
|
||||||
<div class="notification is-success">
|
<message variant="success">
|
||||||
{{ $t('user.auth.resetPasswordSuccess') }}
|
{{ $t('user.auth.resetPasswordSuccess') }}
|
||||||
</div>
|
</message>
|
||||||
<x-button :to="{ name: 'user.login' }">
|
<x-button :to="{ name: 'user.login' }">
|
||||||
{{ $t('user.auth.login') }}
|
{{ $t('user.auth.login') }}
|
||||||
</x-button>
|
</x-button>
|
||||||
|
@ -57,6 +57,7 @@ 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 { useTitle } from '@/composables/useTitle'
|
||||||
|
import Message from '@/components/misc/message'
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
useTitle(() => t('user.auth.resetPassword'))
|
useTitle(() => t('user.auth.resetPassword'))
|
||||||
|
|
Loading…
Reference in a new issue