diff --git a/front/src/components/requests/Card.vue b/front/src/components/requests/Card.vue new file mode 100644 index 0000000000000000000000000000000000000000..deb9c3fe093a58cc6913805ad4fcbe9bed4da58d --- /dev/null +++ b/front/src/components/requests/Card.vue @@ -0,0 +1,61 @@ +<template> + <div :class="['ui', {collapsed: collapsed}, 'card']"> + <div class="content"> + <div class="header">{{ request.artist_name }}</div> + <div class="description"> + <div + v-if="request.albums" v-html="$options.filters.markdown(request.albums)"></div> + <div v-if="request.comment" class="ui comments"> + <comment + :user="request.user" + :content="request.comment" + :date="request.creation_date"></comment> + </div> + </div> + </div> + <div class="extra content"> + <span > + <i v-if="request.status === 'pending'" class="hourglass start icon"></i> + <i v-if="request.status === 'accepted'" class="hourglass half icon"></i> + <i v-if="request.status === 'imported'" class="check icon"></i> + {{ request.status | capitalize }} + </span> + <button + @click="createImport" + v-if="request.status === 'pending' && importAction && $store.state.auth.availablePermissions['import.launch']" + class="ui mini basic green right floated button">Create import</button> + + </div> + </div> +</template> + +<script> +import Comment from '@/components/discussion/Comment' + +export default { + props: { + request: {type: Object, required: true}, + importAction: {type: Boolean, default: true} + }, + components: { + Comment + }, + data () { + return { + collapsed: true + } + }, + methods: { + createImport () { + this.$router.push({ + name: 'library.import.launch', + query: {request: this.request.id}}) + } + } +} +</script> + +<!-- Add "scoped" attribute to limit CSS to this component only --> +<style scoped> + +</style> diff --git a/front/src/components/requests/RequestsList.vue b/front/src/components/requests/RequestsList.vue new file mode 100644 index 0000000000000000000000000000000000000000..cb3e9af00bc40d0301b23dcb446576a567e7f153 --- /dev/null +++ b/front/src/components/requests/RequestsList.vue @@ -0,0 +1,163 @@ +<template> + <div> + <div class="ui vertical stripe segment"> + <h2 class="ui header">Music requests</h2> + <div :class="['ui', {'loading': isLoading}, 'form']"> + <div class="fields"> + <div class="field"> + <label>Search</label> + <input type="text" v-model="query" placeholder="Enter an artist name, a username..."/> + </div> + <div class="field"> + <label>Ordering</label> + <select class="ui dropdown" v-model="ordering"> + <option v-for="option in orderingOptions" :value="option[0]"> + {{ option[1] }} + </option> + </select> + </div> + <div class="field"> + <label>Ordering direction</label> + <select class="ui dropdown" v-model="orderingDirection"> + <option value="">Ascending</option> + <option value="-">Descending</option> + </select> + </div> + <div class="field"> + <label>Results per page</label> + <select class="ui dropdown" v-model="paginateBy"> + <option :value="parseInt(12)">12</option> + <option :value="parseInt(25)">25</option> + <option :value="parseInt(50)">50</option> + </select> + </div> + </div> + </div> + <div class="ui hidden divider"></div> + <div v-if="result" class="ui stackable three column grid"> + <div + v-if="result.results.length > 0" + v-for="request in result.results" + :key="request.id" + class="column"> + <request-card class="fluid" :request="request"></request-card> + </div> + </div> + <div class="ui center aligned basic segment"> + <pagination + v-if="result && result.results.length > 0" + @page-changed="selectPage" + :current="page" + :paginate-by="paginateBy" + :total="result.count" + ></pagination> + </div> + </div> + </div> +</template> + +<script> +import axios from 'axios' +import _ from 'lodash' +import $ from 'jquery' + +import logger from '@/logging' + +import OrderingMixin from '@/components/mixins/Ordering' +import PaginationMixin from '@/components/mixins/Pagination' +import RequestCard from '@/components/requests/Card' +import Pagination from '@/components/Pagination' + +const FETCH_URL = 'requests/import-requests/' + +export default { + mixins: [OrderingMixin, PaginationMixin], + props: { + defaultQuery: {type: String, required: false, default: ''} + }, + components: { + RequestCard, + Pagination + }, + data () { + let defaultOrdering = this.getOrderingFromString(this.defaultOrdering || '-creation_date') + return { + isLoading: true, + result: null, + page: parseInt(this.defaultPage), + query: this.defaultQuery, + paginateBy: parseInt(this.defaultPaginateBy || 12), + orderingDirection: defaultOrdering.direction, + ordering: defaultOrdering.field, + orderingOptions: [ + ['creation_date', 'Creation date'], + ['artist_name', 'Artist name'], + ['user__username', 'User'] + ] + } + }, + created () { + this.fetchData() + }, + mounted () { + $('.ui.dropdown').dropdown() + }, + methods: { + updateQueryString: _.debounce(function () { + this.$router.replace({ + query: { + query: this.query, + page: this.page, + paginateBy: this.paginateBy, + ordering: this.getOrderingAsString() + } + }) + }, 500), + fetchData: _.debounce(function () { + var self = this + this.isLoading = true + let url = FETCH_URL + let params = { + page: this.page, + page_size: this.paginateBy, + search: this.query, + ordering: this.getOrderingAsString() + } + logger.default.debug('Fetching request...') + axios.get(url, {params: params}).then((response) => { + self.result = response.data + self.isLoading = false + }) + }, 500), + selectPage: function (page) { + this.page = page + } + }, + watch: { + page () { + this.updateQueryString() + this.fetchData() + }, + paginateBy () { + this.updateQueryString() + this.fetchData() + }, + ordering () { + this.updateQueryString() + this.fetchData() + }, + orderingDirection () { + this.updateQueryString() + this.fetchData() + }, + query () { + this.updateQueryString() + this.fetchData() + } + } +} +</script> + +<!-- Add "scoped" attribute to limit CSS to this component only --> +<style scoped> +</style> diff --git a/front/src/router/index.js b/front/src/router/index.js index bf59b8ee86213029611624ef9cc2cb62bc355d7d..ea8854bbe48e4f06ec4369a2853b3e98271df733 100644 --- a/front/src/router/index.js +++ b/front/src/router/index.js @@ -17,6 +17,7 @@ import LibraryRadios from '@/components/library/Radios' import RadioBuilder from '@/components/library/radios/Builder' import BatchList from '@/components/library/import/BatchList' import BatchDetail from '@/components/library/import/BatchDetail' +import RequestsList from '@/components/requests/RequestsList' import Favorites from '@/components/favorites/List' @@ -100,6 +101,7 @@ export default new Router({ component: LibraryImport, props: (route) => ({ source: route.query.source, + request: route.query.request, mbType: route.query.type, mbId: route.query.id }) }, @@ -110,7 +112,21 @@ export default new Router({ children: [ ] }, - { path: 'import/batches/:id', name: 'library.import.batches.detail', component: BatchDetail, props: true } + { path: 'import/batches/:id', name: 'library.import.batches.detail', component: BatchDetail, props: true }, + { + path: 'requests/', + name: 'library.requests', + component: RequestsList, + props: (route) => ({ + defaultOrdering: route.query.ordering, + defaultQuery: route.query.query, + defaultPaginateBy: route.query.paginateBy, + defaultPage: route.query.page, + defaultStatus: route.query.status || 'pending' + }), + children: [ + ] + } ] }, { path: '*', component: PageNotFound }