Library/frontend/src/components/Books.vue

301 lines
7.8 KiB
Vue

<template>
<div v-if="user.authenticated">
<h1 v-lang.books.title></h1>
<div class="fullscreen-loader-wrapper" v-if="loading">
<div class="half-circle-spinner">
<div class="circle circle-1"></div>
<div class="circle circle-2"></div>
</div>
</div>
<div v-if="!loading">
<router-link to="/books/add" class="ui green labeled icon button" style="float: right;">
<i class="plus icon"></i>
<span v-lang.books.add></span>
</router-link>
<button @click="loadBooks()" class="ui teal labeled icon button" style="float: right;">
<i class="refresh icon"></i>
<span v-lang.general.refresh></span>
</button>
<form id="search">
<div class="ui icon input">
<input v-bind:placeholder="langGeneral.search" type="text" v-model="searchQuery" v-focus>
<i class="search icon"></i>
</div>
</form>
<paginate
name="books"
:list="filteredData"
:per="35"
tag="div"
>
<grid
:data="paginated('books')"
:columns="gridColumns"
:buttons="gridButtons"
:btnclick="gridBtnClicked"
>
</grid>
</paginate>
<div class="pagination-container">
<paginate-links
tag="div"
for="books"
:hide-single-page="true"
:classes="{
'ul': ['ui', 'pagination', 'menu'],
'li': 'item',
'li a': 'pagination-link'
}"
>
</paginate-links>
<div v-if="$refs.paginator" v-lang.general.searchResultCount="$refs.paginator.pageItemsCount">
</div>
</div>
</div>
<modal
v-if="showModal"
@close="showModal = false"
v-on:submit="deleteBtnSuccess()">
<span slot="header" v-lang.books.deleteHeader></span>
<p slot="text" v-lang.books.deleteMsg></p>
</modal>
</div>
</template>
<script>
import auth from '../auth'
import {HTTP} from '../http-common'
import router from '../router'
export default {
name: 'Books',
data () {
return {
user: auth.user,
books: [],
searchQuery: '',
gridColumns: [],
gridButtons: [
{
text: '',
icon: 'trash',
action: this.deleteBook,
css: 'ui red icon button'
},
{
text: '',
icon: 'edit',
action: this.editBook,
css: 'ui blue icon button'
}
],
loading: false,
paginate: ['books'],
allStatus: [],
showModal: false
}
},
created () {
this.loadStatus()
this.loadBooks()
// Set the delete button description
this.gridButtons[0].text = this.translate('general').delete
let gc = this.translate('books').gridColumns
this.gridColumns = [
gc.title,
gc.isbn,
gc.year,
gc.price,
gc.authors,
gc.publisher,
gc.quantity,
gc.status
]
document.title = this.translate('nav').books
},
watch: {
// call again the method if the route changes
'$route': 'loadBooks'
},
computed: {
filteredData: function () {
let filterKey = this.searchQuery && this.searchQuery.toLowerCase()
let data = this.books
if (filterKey) {
data = data.filter(function (row) {
return Object.keys(row).some(function (key) {
if (row[key].content) {
return String(row[key].content).toLowerCase().indexOf(filterKey) > -1
} else {
return String(row[key]).toLowerCase().indexOf(filterKey) > -1
}
})
})
}
return data
},
langGeneral () {
return this.translate('general')
},
langBook () {
return this.translate('books')
}
},
methods: {
errorNotification (e) {
// Build the notification text from error response
let err = e.message
if (e.response.data.message) {
err += '<br/>' + e.response.data.message
}
// Fire a notification
this.$notify({
type: 'error',
title: this.langGeneral.error,
text: err
})
},
loadBooks () {
this.loading = true
this.books = []
HTTP.get(`books`)
.then(response => {
let bs = response.data
let i = 0
// Loop throught the data we got from our API and prepare an array to display all books
for (const b in bs) {
this.books[i] = {
id: {content: bs[b].id, hide: true}, // Don't show the ID
title: {content: bs[b].title, link: '/books/' + bs[b].id}, // Add a link to the element
isbn: {content: bs[b].isbn}, // We can also just use the content column
year: bs[b].year,
price: bs[b].price + '€',
author: '',
publisher: bs[b].publisher.name,
quantity: bs[b].quantity,
status: bs[b].status
}
// Get all authors and concat them into one singe string
let authors = bs[b].authors
for (const au in authors) {
this.books[i].author += authors[au].forename + ' ' + authors[au].lastname
if ((authors.length - 1) > au) {
this.books[i].author += ', '
}
}
// Make Status a name, not an id
this.books[i].status = this.getStatusByID(this.books[i].status)
// increment dat shit
i++
}
this.loading = false
})
.catch(e => {
this.loading = false
this.errorNotification(e)
})
},
loadStatus: function () {
HTTP.get('status')
.then(response => {
this.allStatus = response.data
})
.catch(e => {
if (!e.response.data.message) {
e.response.data = { message: 'Could not get Status.' }
}
this.errorNotification(e)
})
},
getStatusByID: function (id) {
// TODO: is there a better way to do this?
for (const i in this.allStatus) {
if (this.allStatus[i].ID === id) {
return this.allStatus[i].Name
}
}
return ''
},
gridBtnClicked (opt, gridObject) {
opt.action(gridObject)
},
deleteBtnSuccess () { // Event helper function
this.$emit('delete-submit')
},
deleteBook (obj) {
this.showModal = true
this.$on('delete-submit', function () {
this.loading = true
// Prevent deleting already deleted books
if (obj) {
HTTP.delete('books/' + obj.id.content, { headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')} })
.then(response => {
if (response.status === 200 && response.data.message === 'success') {
// Fire a notification
this.$notify({
type: 'success',
title: this.langGeneral.success,
text: this.langBook.deleteSuccess
})
this.loadBooks()
}
})
.catch(e => {
this.errorNotification(e)
this.loadBooks()
})
}
obj = null
this.showModal = false
})
},
editBook (book) {
router.push({ name: 'book-edit', params: { id: book.id.content } })
}
}
}
</script>
<style>
a.pagination-link{
margin: -5px -1.14286em -18px;
display: block;
position: absolute;
cursor: pointer;
padding: 0.928571em 1.14286em;
color: rgba(0,0,0,.87);
-webkit-transition: background-color 200ms; /* Safari */
transition: background-color 200ms;
}
a.pagination-link:hover{
background: rgba(0,0,0,.02);
}
.pagination{
padding: 0;
}
.pagination-container{
margin-top: 1rem;
text-align: center;
}
#search{
margin-bottom: 1rem;
}
</style>