implemented toasts for error messages
This commit is contained in:
parent
79deed25bc
commit
cecda6194d
5 changed files with 86 additions and 5 deletions
|
@ -17,6 +17,7 @@
|
|||
"dotenv-webpack": "^1.7.0",
|
||||
"jquery": "^3.4.1",
|
||||
"lodash": "^4.17.15",
|
||||
"luxon": "^1.21.3",
|
||||
"node-sass": "^4.13.0",
|
||||
"popper.js": "^1.16.0",
|
||||
"ramda": "^0.26.1",
|
||||
|
|
11
src/App.vue
11
src/App.vue
|
@ -3,22 +3,27 @@
|
|||
<AddItem v-if="addModalOpen" @close="closeAddModal()" isModal="true"/>
|
||||
<Navbar @addClicked="openAddModal()"/>
|
||||
<router-view/>
|
||||
<div aria-live="polite" aria-atomic="true" class="d-flex justify-content-end align-items-start fixed-top mx-1 my-5 py-3" style="min-height: 200px;">
|
||||
<Toast v-for="toast in toasts" :key="toast" :title="toast.title" :message="toast.message" :color="toast.color" @close="removeToast(toast.key)"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Navbar from '@/components/Navbar';
|
||||
import AddItem from '@/components/AddItem';
|
||||
import { mapState } from 'vuex';
|
||||
import Toast from './components/Toast';
|
||||
import { mapState, mapMutations } from 'vuex';
|
||||
|
||||
export default {
|
||||
name: 'app',
|
||||
components: { Navbar, AddItem },
|
||||
computed: mapState(['loadedItems', 'layout']),
|
||||
components: { Toast, Navbar, AddItem },
|
||||
computed: mapState(['loadedItems', 'layout', 'toasts']),
|
||||
data: () => ({
|
||||
addModalOpen: false
|
||||
}),
|
||||
methods: {
|
||||
...mapMutations(['removeToast']),
|
||||
openAddModal() {
|
||||
this.addModalOpen = true;
|
||||
},
|
||||
|
|
48
src/components/Toast.vue
Normal file
48
src/components/Toast.vue
Normal file
|
@ -0,0 +1,48 @@
|
|||
<template>
|
||||
<div class="toast" :class="color && ('border-' + color)" role="alert" ref="toast" data-autohide="false">
|
||||
<div class="toast-header">
|
||||
<strong class="mr-auto pr-3">{{ title }}</strong>
|
||||
<small>{{ displayTime }}</small>
|
||||
<button type="button" class="ml-2 mb-1 close" @click="close()">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="toast-body" v-html="message">{{ message }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import $ from 'jquery';
|
||||
import 'bootstrap/js/dist/toast';
|
||||
import { DateTime } from 'luxon';
|
||||
|
||||
export default {
|
||||
name: 'Toast',
|
||||
props: ['title', 'message', 'color'],
|
||||
data: () => ({
|
||||
creationTime: DateTime.local(),
|
||||
displayTime: 'just now',
|
||||
timer: undefined
|
||||
}),
|
||||
mounted() {
|
||||
const { toast } = this.$refs;
|
||||
$(toast).toast('show');
|
||||
this.timer = setInterval(this.updateDisplayTime, 1000);
|
||||
},
|
||||
methods: {
|
||||
close() {
|
||||
const { toast } = this.$refs;
|
||||
$(toast).toast('hide');
|
||||
window.setTimeout(() => {
|
||||
this.$emit('close');
|
||||
}, 500);
|
||||
},
|
||||
updateDisplayTime() {
|
||||
this.displayTime = this.creationTime.toRelative();
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
clearInterval(this.timer);
|
||||
}
|
||||
};
|
||||
</script>
|
|
@ -12,16 +12,32 @@ const axios = AxiosBootstrap.create({
|
|||
});
|
||||
|
||||
axios.interceptors.response.use(response => response, error => {
|
||||
console.log('error interceptor fired');
|
||||
console.error(error); // todo: toast error
|
||||
console.log(Object.entries(error));
|
||||
|
||||
if (error.isAxiosError) {
|
||||
const message = `
|
||||
<h3>A HTTP ${error.config.method} request failed.</h3>
|
||||
<p>url: ${error.config.url}</p>
|
||||
<p>timeout: ${!!error.request.timeout}</p>
|
||||
<p>response-body: ${error.response && error.response.body}</p>
|
||||
`;
|
||||
store.commit('createToast', {title: 'HTTP Error', message, color: 'danger'});
|
||||
} else {
|
||||
store.commit('createToast', {title: 'Unknown Error', message: error.toString(), color: 'danger'});
|
||||
}
|
||||
return Promise.reject(error);
|
||||
});
|
||||
|
||||
const store = new Vuex.Store({
|
||||
state: {
|
||||
keyIncrement: 0,
|
||||
events: [],
|
||||
layout: 'cards',
|
||||
loadedItems: [],
|
||||
loadedBoxes: [],
|
||||
toasts: []
|
||||
},
|
||||
getters: {
|
||||
getEventSlug: state => state.route && state.route.params.event? state.route.params.event : state.events.length ? state.events[0].slug : '36C3',
|
||||
|
@ -54,6 +70,13 @@ const store = new Vuex.Store({
|
|||
},
|
||||
appendItem(state, item) {
|
||||
state.loadedItems.push(item);
|
||||
},
|
||||
createToast(state, { title, message, color }) {
|
||||
state.toasts.push({ title, message, color, key: state.keyIncrement });
|
||||
state.keyIncrement += 1;
|
||||
},
|
||||
removeToast(state, key) {
|
||||
state.toasts = state.toasts.filter(toast => toast.key !== key);
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
|
@ -103,5 +126,4 @@ export default store;
|
|||
store.dispatch('loadEvents').then(() =>{
|
||||
store.dispatch('loadEventItems');
|
||||
store.dispatch('loadBoxes');
|
||||
}
|
||||
);
|
||||
});
|
||||
|
|
|
@ -5074,6 +5074,11 @@ lru-cache@^5.1.1:
|
|||
dependencies:
|
||||
yallist "^3.0.2"
|
||||
|
||||
luxon@^1.21.3:
|
||||
version "1.21.3"
|
||||
resolved "https://registry.yarnpkg.com/luxon/-/luxon-1.21.3.tgz#f1d5c2a7e855d039836cf4954f883ecac8fc4727"
|
||||
integrity sha512-lLRwNcNnkZLuv13A1FUuZRZmTWF7ro2ricYvb0L9cvBYHPvZhQdKwrYnZzi103D2XKmlVmxWpdn2wfIiOt2YEw==
|
||||
|
||||
make-dir@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5"
|
||||
|
|
Loading…
Reference in a new issue