Show error message in web

This commit is contained in:
Radhi Fadlillah 2018-02-14 18:50:53 +07:00
parent be95bdf227
commit dd2c302153
4 changed files with 118 additions and 61 deletions

File diff suppressed because one or more lines are too long

View file

@ -24,64 +24,72 @@
<i class="fas fa-search fa-fw"></i> <i class="fas fa-search fa-fw"></i>
</a> </a>
</div> </div>
<div id="header-menu" v-if="!loading">
<a href="#" @click="loadData">
<i class="fas fa-sync fa-fw"></i>
</a>
</div>
</div> </div>
<div id="main"> <div id="main">
<div id="input-bookmark"> <template v-if="!loading && error === ''">
<p v-if="inputBookmark.url !== ''">{{inputBookmark.id === -1 ? 'New bookmark' : 'Edit bookmark'}}</p> <div id="input-bookmark" v-if="!loading">
<input type="text" ref="inputURL" v-model="inputBookmark.url" placeholder="URL for the bookmark"> <p v-if="inputBookmark.url !== ''">{{inputBookmark.id === -1 ? 'New bookmark' : 'Edit bookmark'}}</p>
<template v-if="inputBookmark.url !== ''"> <input type="text" ref="inputURL" v-model="inputBookmark.url" placeholder="URL for the bookmark">
<input type="text" v-model="inputBookmark.title" placeholder="Custom bookmark title (optional)"> <template v-if="inputBookmark.url !== ''">
<input type="text" v-model="inputBookmark.tags" placeholder="Space separated tags for this bookmark (optional)"> <input type="text" v-model="inputBookmark.title" placeholder="Custom bookmark title (optional)">
<textarea name="excerpt" v-model="inputBookmark.excerpt" placeholder="Excerpt for this bookmark (optional)"></textarea> <input type="text" v-model="inputBookmark.tags" placeholder="Space separated tags for this bookmark (optional)">
<div class="button-area"> <textarea name="excerpt" v-model="inputBookmark.excerpt" placeholder="Excerpt for this bookmark (optional)"></textarea>
<div class="spacer"></div> <p v-if="inputBookmark.error !== ''" class="error-message">{{inputBookmark.error}}</p>
<a v-if="inputBookmark.loading"> <div class="button-area">
<i class="fas fa-fw fa-spinner fa-spin"></i> <div class="spacer"></div>
</a> <a v-if="inputBookmark.loading">
<template v-else> <i class="fas fa-fw fa-spinner fa-spin"></i>
<a class="button" @click="clearInputBookmark">Cancel</a> </a>
<a class="button" @click="saveBookmark">Done</a> <template v-else>
</template> <a class="button" @click="clearInputBookmark">Cancel</a>
</div> <a class="button" @click="saveBookmark">Done</a>
</template> </template>
</div>
<div id="grid">
<div v-for="column in gridColumns" class="column">
<div v-for="item in column" class="bookmark" :ref="'bookmark-'+item.index">
<a href="#" class="checkbox">
<i class="fas fa-check"></i>
</a>
<a class="bookmark-metadata" :class="{'has-image':item.imageURL !== ''}" :style="bookmarkImage(item)" :href="item.url">
<p class="bookmark-time">{{bookmarkTime(item)}}</p>
<p class="bookmark-title">{{item.title}}</p>
<p class="bookmark-url">{{getDomainURL(item.url)}}</p>
</a>
<p v-if="item.excerpt !== ''" class="bookmark-excerpt">{{item.excerpt}}</p>
<div v-if="item.tags.length > 0" class="bookmark-tags">
<a v-for="tag in item.tags" href="#">{{tag.name}}</a>
</div> </div>
<div class="bookmark-menu"> </template>
<a href="#" @click="editBookmark(item.index)"> </div>
<i class="fas fa-pencil-alt"></i> <div id="grid" v-if="!loading">
<div v-for="column in gridColumns" class="column">
<div v-for="item in column" class="bookmark" :ref="'bookmark-'+item.index">
<a href="#" class="checkbox">
<i class="fas fa-check"></i>
</a> </a>
<a href="#" @click="deleteBookmark(item.index)"> <a class="bookmark-metadata" :class="{'has-image':item.imageURL !== ''}" :style="bookmarkImage(item)" :href="item.url">
<i class="far fa-trash-alt"></i> <p class="bookmark-time">{{bookmarkTime(item)}}</p>
</a> <p class="bookmark-title">{{item.title}}</p>
<a href="#"> <p class="bookmark-url">{{getDomainURL(item.url)}}</p>
<i class="fas fa-history"></i>
</a> </a>
<p v-if="item.excerpt !== ''" class="bookmark-excerpt">{{item.excerpt}}</p>
<div v-if="item.tags.length > 0" class="bookmark-tags">
<a v-for="tag in item.tags" href="#">{{tag.name}}</a>
</div>
<div class="bookmark-menu">
<a href="#" @click="editBookmark(item.index)">
<i class="fas fa-pencil-alt"></i>
</a>
<a href="#" @click="deleteBookmark(item.index)">
<i class="far fa-trash-alt"></i>
</a>
<a href="#">
<i class="fas fa-history"></i>
</a>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </template>
<div id="progress-bar"> <div v-if="loading || error !== ''" id="message-bar">
<i v-if="loading" class="fas fa-fw fa-spinner fa-spin"></i> <i v-if="loading" class="fas fa-fw fa-spinner fa-spin"></i>
<a href="#">Load more bookmarks</a> <p v-if="error !== ''" class="error-message">{{error}}</p>
</div> </div>
</div> </div>
<div v-if="dialog.visible" id="dialog-overlay"> <div v-if="dialog.visible" id="dialog-overlay">
<div id="dialog"> <div id="dialog">
<p id="dialog-title">{{dialog.title}}</p> <p id="dialog-title" :class="{'error-message': dialog.isError}">{{dialog.title}}</p>
<p v-html="dialog.content" id="dialog-content"></p> <p v-html="dialog.content" id="dialog-content"></p>
<div id="dialog-button"> <div id="dialog-button">
<div class="spacer"></div> <div class="spacer"></div>
@ -98,12 +106,13 @@
</div> </div>
<script> <script>
var instance = axios.create(); var instance = axios.create();
instance.defaults.timeout = 2500; instance.defaults.timeout = 10000;
var app = new Vue({ var app = new Vue({
el: '#app', el: '#app',
data: { data: {
windowWidth: 0, windowWidth: 0,
error: "",
loading: true, loading: true,
bookmarks: [], bookmarks: [],
inputBookmark: { inputBookmark: {
@ -113,11 +122,13 @@
title: "", title: "",
tags: "", tags: "",
excerpt: "", excerpt: "",
error: "",
loading: false loading: false
}, },
dialog: { dialog: {
visible: false, visible: false,
loading: false, loading: false,
isError: false,
title: '', title: '',
content: '', content: '',
mainChoice: '', mainChoice: '',
@ -128,6 +139,7 @@
}, },
methods: { methods: {
loadData: function () { loadData: function () {
this.error = '';
this.loading = true; this.loading = true;
instance.get('/api/bookmarks') instance.get('/api/bookmarks')
.then(function (response) { .then(function (response) {
@ -135,8 +147,9 @@
app.bookmarks = response.data; app.bookmarks = response.data;
}) })
.catch(function (error) { .catch(function (error) {
var errorMsg = error.response ? error.response.data : error.message;
app.loading = false; app.loading = false;
console.log(error); app.error = errorMsg.trim();
}); });
}, },
saveBookmark: function () { saveBookmark: function () {
@ -176,7 +189,9 @@
app.clearInputBookmark(); app.clearInputBookmark();
}) })
.catch(function (error) { .catch(function (error) {
console.log(error); var errorMsg = error.response ? error.response.data : error.message;
app.inputBookmark.loading = false;
app.inputBookmark.error = errorMsg.trim();
}); });
}, },
editBookmark: function (idx) { editBookmark: function (idx) {
@ -202,6 +217,7 @@
var bookmark = this.bookmarks[idx]; var bookmark = this.bookmarks[idx];
this.dialog.visible = true; this.dialog.visible = true;
this.dialog.isError = false;
this.dialog.loading = false; this.dialog.loading = false;
this.dialog.title = "Delete Bookmark"; this.dialog.title = "Delete Bookmark";
this.dialog.content = "Delete <b>\"" + bookmark.title.trim() + "\"</b> from bookmarks ? This action is irreversible."; this.dialog.content = "Delete <b>\"" + bookmark.title.trim() + "\"</b> from bookmarks ? This action is irreversible.";
@ -222,9 +238,8 @@
}); });
}) })
.catch(function (error) { .catch(function (error) {
app.dialog.loading = false; var errorMsg = error.response ? error.response.data : error.message;
app.dialog.visible = false; app.showDialogError("Error Deleting Bookmark", errorMsg.trim());
console.log(error);
}); });
}; };
this.dialog.secondAction = function () { this.dialog.secondAction = function () {
@ -244,6 +259,7 @@
this.inputBookmark.title = ""; this.inputBookmark.title = "";
this.inputBookmark.tags = ""; this.inputBookmark.tags = "";
this.inputBookmark.excerpt = ""; this.inputBookmark.excerpt = "";
this.inputBookmark.error = "";
this.inputBookmark.loading = false; this.inputBookmark.loading = false;
if (idx !== -1) app.$nextTick(function () { if (idx !== -1) app.$nextTick(function () {
@ -286,6 +302,19 @@
hostname = hostname.split('?')[0]; hostname = hostname.split('?')[0];
return hostname; return hostname;
},
showDialogError: function (title, msg) {
this.dialog.isError = true;
this.dialog.visible = true;
this.dialog.loading = false;
this.dialog.title = title;
this.dialog.content = msg;
this.dialog.mainChoice = "OK"
this.dialog.secondChoice = ""
this.dialog.mainAction = function () {
app.dialog.visible = false;
}
this.dialog.secondAction = function () {}
} }
}, },
computed: { computed: {

View file

@ -75,6 +75,21 @@
padding: 8px 16px; padding: 8px 16px;
} }
} }
#header-menu {
display: flex;
flex-flow: row nowrap;
a {
line-height: @headerHeight;
padding: 0 16px;
color: @linkColor;
font-size: 0.9em;
cursor: pointer;
&:hover {
color: @main;
background-color: @appBg;
}
}
}
} }
#main { #main {
margin-top: @headerHeight; margin-top: @headerHeight;
@ -92,26 +107,31 @@
} }
} }
} }
#progress-bar { #message-bar {
display: flex; display: flex;
flex-flow: column; flex-flow: column;
align-items: center; align-items: center;
padding: 32px; padding: 32px;
justify-content: center;
position: absolute;
top: 50%;
left: 0;
width: 100%;
margin-top: -60px;
height: 120px;
i { i {
color: @fontLightColor; color: @fontLightColor;
font-size: 3em; font-size: 3em;
} }
a {
color: @main !important;
font-size: 0.9em;
&:hover {
color: @accent !important;
}
}
} }
} }
} }
.error-message {
color: @main !important;
font-size: 0.9em;
}
.bookmark { .bookmark {
background-color: @contentBg; background-color: @contentBg;
border: 1px solid @border; border: 1px solid @border;
@ -268,6 +288,13 @@
font-weight: 600; font-weight: 600;
text-transform: uppercase; text-transform: uppercase;
padding: 16px; padding: 16px;
&.error-message {
color: @main;
font-size: 0.9em;
border-bottom: 1px solid @border;
font-weight: 500;
text-transform: none;
}
} }
input[type=text], input[type=text],
textarea { textarea {
@ -326,6 +353,7 @@
font-weight: 600; font-weight: 600;
text-transform: uppercase; text-transform: uppercase;
padding: 16px; padding: 16px;
font-size: 1em;
border-bottom: 1px solid @border; border-bottom: 1px solid @border;
} }
#dialog-content { #dialog-content {

View file

@ -9,7 +9,7 @@
@fontLightColor: #6F757A; @fontLightColor: #6F757A;
@fontSize: 0.9em; @fontSize: 0.9em;
@headerHeight: 70px; @headerHeight: 70px;
@main: #3F51B5; @main: #F44336;
@accent: #F44336; @accent: #F44336;
@headerShadow: rgba(0, 0, 0, 0.3); @headerShadow: rgba(0, 0, 0, 0.3);
// Mixin // Mixin