Added error handling and handle async component failing
This commit is contained in:
parent
c898d9b702
commit
670edf3006
9 changed files with 254 additions and 2 deletions
36
src/components/error_modal/error_modal.js
Normal file
36
src/components/error_modal/error_modal.js
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
import DialogModal from 'src/components/dialog_modal/dialog_modal.vue'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { faCircleXmark } from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
library.add(faCircleXmark)
|
||||
|
||||
/**
|
||||
* This component emits the following events:
|
||||
* cancelled, emitted when the action should not be performed;
|
||||
* accepted, emitted when the action should be performed;
|
||||
*
|
||||
* The caller should close this dialog after receiving any of the two events.
|
||||
*/
|
||||
const ErrorModal = {
|
||||
components: {
|
||||
DialogModal,
|
||||
},
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
},
|
||||
clearText: {
|
||||
type: String,
|
||||
},
|
||||
recoverText: {
|
||||
type: String,
|
||||
},
|
||||
error: {
|
||||
type: Error,
|
||||
},
|
||||
},
|
||||
emits: ['clear', 'recover']
|
||||
}
|
||||
|
||||
export default ErrorModal
|
||||
100
src/components/error_modal/error_modal.vue
Normal file
100
src/components/error_modal/error_modal.vue
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
<template>
|
||||
<DialogModal
|
||||
v-body-scroll-lock="true"
|
||||
class="error-modal"
|
||||
@cancel="onCancel"
|
||||
>
|
||||
<template #header>
|
||||
<span v-text="title ?? $t('general.generic_error')" />
|
||||
</template>
|
||||
|
||||
<div class="content">
|
||||
<FAIcon
|
||||
class="error-icon"
|
||||
icon="circle-xmark"
|
||||
size="3x"
|
||||
fixed-width
|
||||
/>
|
||||
<div class="text">
|
||||
<slot>
|
||||
<p>
|
||||
<strong><code v-text="error.name" /></strong>
|
||||
{{ ' - ' }}
|
||||
<span v-text="error.message" />
|
||||
</p>
|
||||
|
||||
<details open>
|
||||
<summary>{{ $t('general.generic_error_details') }}</summary>
|
||||
<code class="stack pre" v-text="error.stack" />
|
||||
</details>
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
<div class="below">
|
||||
<slot name="below" />
|
||||
</div>
|
||||
<template #footer>
|
||||
<slot name="footerLeft" />
|
||||
|
||||
<button
|
||||
v-if="recoverText"
|
||||
class="btn button-default"
|
||||
@click.prevent="$emit('recover')"
|
||||
v-text="recoverText"
|
||||
/>
|
||||
|
||||
<button
|
||||
class="btn button-default"
|
||||
@click.prevent="$emit('clear')"
|
||||
v-text="clearText ?? $t('general.close')"
|
||||
/>
|
||||
</template>
|
||||
</DialogModal>
|
||||
</template>
|
||||
|
||||
<script src="./error_modal.js"></script>
|
||||
<style lang="scss">
|
||||
.error-modal {
|
||||
.error-icon {
|
||||
margin-left: 0.75rem;
|
||||
margin-top: 0.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-align: left;
|
||||
justify-content: center;
|
||||
line-height: 1.5;
|
||||
|
||||
p {
|
||||
margin-top: 0.75em;
|
||||
margin-bottom: 0.75em;
|
||||
|
||||
&:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.stack {
|
||||
margin: 0;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.below:not(:empty) {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.text {
|
||||
max-width: 50ch;
|
||||
margin-left: 0.5em;
|
||||
margin-right: 3.5em;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
52
src/components/global_error/global_error.js
Normal file
52
src/components/global_error/global_error.js
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
import ErrorModal from 'src/components/error_modal/error_modal.vue'
|
||||
|
||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||
|
||||
import { mapState, mapActions } from 'pinia'
|
||||
|
||||
const GlobalError = {
|
||||
components: {
|
||||
ErrorModal,
|
||||
},
|
||||
computed: {
|
||||
title() {
|
||||
if (this.globalError == null) return null
|
||||
return this.globalError.title && this.$t(this.globalError.title)
|
||||
},
|
||||
content() {
|
||||
if (this.globalError == null) return null
|
||||
if (this.globalError.content) {
|
||||
return this.$t(this.globalError.content, [this.globalError.error])
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
},
|
||||
details() {
|
||||
if (this.globalError == null) return null
|
||||
if (this.globalError.error != null) {
|
||||
return this.globalError.error.toString() + '\n\n' + this.globalError.error.stack
|
||||
} else {
|
||||
return this.globalError.details
|
||||
}
|
||||
},
|
||||
recoverText() {
|
||||
if (this.globalError == null) return null
|
||||
if (this.globalError.recoverText == null) return null
|
||||
return this.$t(this.globalError.recoverText)
|
||||
},
|
||||
...mapState(useInterfaceStore, ['globalError']),
|
||||
},
|
||||
methods: {
|
||||
clear() {
|
||||
this.globalError.clear?.()
|
||||
this.clearGlobalError()
|
||||
},
|
||||
recover() {
|
||||
this.globalError.recover?.()
|
||||
this.clearGlobalError()
|
||||
},
|
||||
...mapActions(useInterfaceStore, ['clearGlobalError']),
|
||||
},
|
||||
}
|
||||
|
||||
export default GlobalError
|
||||
23
src/components/global_error/global_error.vue
Normal file
23
src/components/global_error/global_error.vue
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<template>
|
||||
<teleport to="#modal">
|
||||
<ErrorModal
|
||||
v-if="globalError"
|
||||
:error="globalError.error"
|
||||
:title="title"
|
||||
:recover-text="recoverText"
|
||||
@recover="recover"
|
||||
@clear="clear"
|
||||
>
|
||||
<template v-if="content">
|
||||
<p>{{ content }}</p>
|
||||
|
||||
<details v-if="details">
|
||||
<summary>{{ $t('general.generic_error_details') }}</summary>
|
||||
<code class="stack pre" v-text="details" />
|
||||
</details>
|
||||
</template>
|
||||
</ErrorModal>
|
||||
</teleport>
|
||||
</template>
|
||||
|
||||
<script src="./global_error.js"></script>
|
||||
Loading…
Add table
Add a link
Reference in a new issue