mirror of
https://github.com/gotson/komga.git
synced 2025-12-20 23:45:11 +01:00
feat(webui): filter series by status
when browsing libraries linked to #48
This commit is contained in:
parent
c96bf19048
commit
c540e56c08
3 changed files with 84 additions and 15 deletions
|
|
@ -1,3 +1,4 @@
|
|||
import { SeriesStatus } from '@/types/common'
|
||||
<template>
|
||||
<div>
|
||||
<v-toolbar flat
|
||||
|
|
@ -12,7 +13,7 @@
|
|||
<v-toolbar-title>
|
||||
<span>{{ library ? library.name : 'All libraries' }}</span>
|
||||
<span class="ml-4 badge-count"
|
||||
v-if="totalElements"
|
||||
v-if="totalElements !== null"
|
||||
>
|
||||
{{ totalElements }}
|
||||
</span>
|
||||
|
|
@ -20,6 +21,33 @@
|
|||
|
||||
<v-spacer/>
|
||||
|
||||
<!-- Filter menu -->
|
||||
<v-menu offset-y
|
||||
:close-on-content-click="false"
|
||||
>
|
||||
<template v-slot:activator="{on}">
|
||||
<v-btn icon v-on="on">
|
||||
<v-icon :color="$_.isEmpty(filterStatus) ? null : 'secondary'"
|
||||
>mdi-filter-variant
|
||||
</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-list>
|
||||
<v-subheader>STATUS</v-subheader>
|
||||
<v-list-item v-for="s in SeriesStatus"
|
||||
:key="s"
|
||||
>
|
||||
<v-list-item-title class="text-capitalize">
|
||||
<v-checkbox v-model="filterStatus"
|
||||
:label="s.toString().toLowerCase()"
|
||||
color="secondary"
|
||||
class="mt-1 ml-2"
|
||||
:value="s"/>
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
|
||||
<!-- Sort menu -->
|
||||
<v-menu offset-y>
|
||||
<template v-slot:activator="{on}">
|
||||
|
|
@ -49,7 +77,7 @@
|
|||
</v-toolbar>
|
||||
|
||||
<v-container fluid class="px-6">
|
||||
<v-row justify="start" ref="content" v-resize="updateCardWidth">
|
||||
<v-row justify="start" ref="content" v-resize="updateCardWidth" v-if="totalElements !== 0">
|
||||
|
||||
<v-skeleton-loader v-for="(s, i) in series"
|
||||
:key="i"
|
||||
|
|
@ -66,6 +94,20 @@
|
|||
</v-skeleton-loader>
|
||||
|
||||
</v-row>
|
||||
|
||||
<!-- Empty state if filter returns no books -->
|
||||
<v-row justify="center" v-else>
|
||||
<div class="text-center">
|
||||
<v-avatar color="grey lighten-3" size="400">
|
||||
<div>
|
||||
<v-icon color="primary" size="140">mdi-book-multiple</v-icon>
|
||||
<h1 class="headline">The active filter has no matches</h1>
|
||||
<p class="body-1">Use the menu above to change the active filter</p>
|
||||
<v-btn color="primary" @click="filterStatus = []">Clear filter</v-btn>
|
||||
</div>
|
||||
</v-avatar>
|
||||
</div>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -73,7 +115,7 @@
|
|||
<script lang="ts">
|
||||
import CardSeries from '@/components/CardSeries.vue'
|
||||
import LibraryActionsMenu from '@/components/LibraryActionsMenu.vue'
|
||||
import { LoadState } from '@/types/common'
|
||||
import { LoadState, SeriesStatus } from '@/types/common'
|
||||
import Vue from 'vue'
|
||||
|
||||
export default Vue.extend({
|
||||
|
|
@ -93,6 +135,8 @@ export default Vue.extend({
|
|||
}] as SortOption[],
|
||||
sortActive: {} as SortActive as SortActive,
|
||||
sortDefault: { key: 'name', order: 'asc' } as SortActive as SortActive,
|
||||
filterStatus: [] as string[],
|
||||
SeriesStatus,
|
||||
cardWidth: 150
|
||||
}
|
||||
},
|
||||
|
|
@ -114,6 +158,12 @@ export default Vue.extend({
|
|||
default: 0
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
filterStatus () {
|
||||
this.updateRoute()
|
||||
this.reloadData(this.libraryId)
|
||||
}
|
||||
},
|
||||
async created () {
|
||||
this.library = await this.getLibraryLazy(this.libraryId)
|
||||
},
|
||||
|
|
@ -127,11 +177,15 @@ export default Vue.extend({
|
|||
|
||||
// restore sort from query param
|
||||
this.sortActive = this.parseQuerySortOrDefault(this.$route.query.sort)
|
||||
|
||||
// restore filter status from query params
|
||||
this.filterStatus = this.parseQueryFilterStatus(this.$route.query.status)
|
||||
},
|
||||
beforeRouteUpdate (to, from, next) {
|
||||
if (to.params.libraryId !== from.params.libraryId) {
|
||||
this.library = this.getLibraryLazy(Number(to.params.libraryId))
|
||||
this.sortActive = this.parseQuerySortOrDefault(to.query.sort)
|
||||
this.filterStatus = this.parseQueryFilterStatus(to.query.status)
|
||||
this.reloadData(Number(to.params.libraryId))
|
||||
}
|
||||
|
||||
|
|
@ -165,6 +219,9 @@ export default Vue.extend({
|
|||
return this.$_.clone(this.sortDefault)
|
||||
}
|
||||
},
|
||||
parseQueryFilterStatus (queryStatus: any): string[] {
|
||||
return queryStatus ? queryStatus.toString().split(',').filter((x: string) => Object.keys(SeriesStatus).includes(x)) : []
|
||||
},
|
||||
async onCardIntersect (entries: any, observer: any, isIntersecting: boolean) {
|
||||
const elementIndex = Number(entries[0].target.dataset['index'])
|
||||
if (isIntersecting) {
|
||||
|
|
@ -181,11 +238,7 @@ export default Vue.extend({
|
|||
const index = (max === undefined ? 0 : max).toString()
|
||||
|
||||
if (this.$route.params.index !== index) {
|
||||
this.$router.replace({
|
||||
name: this.$route.name,
|
||||
params: { libraryId: this.$route.params.libraryId, index: index },
|
||||
query: { sort: `${this.sortActive.key},${this.sortActive.order}` }
|
||||
})
|
||||
this.updateRoute(index)
|
||||
}
|
||||
},
|
||||
reloadData (libraryId: number) {
|
||||
|
|
@ -195,6 +248,16 @@ export default Vue.extend({
|
|||
this.series = Array(this.pageSize).fill(null)
|
||||
this.loadInitialData(libraryId)
|
||||
},
|
||||
updateRoute (index?: string) {
|
||||
this.$router.replace({
|
||||
name: this.$route.name,
|
||||
params: { libraryId: this.$route.params.libraryId, index: index || this.$route.params.index },
|
||||
query: {
|
||||
sort: `${this.sortActive.key},${this.sortActive.order}`,
|
||||
status: `${this.filterStatus}`
|
||||
}
|
||||
})
|
||||
},
|
||||
setSort (sort: SortOption) {
|
||||
if (this.sortActive.key === sort.key) {
|
||||
if (this.sortActive.order === 'desc') {
|
||||
|
|
@ -205,11 +268,7 @@ export default Vue.extend({
|
|||
} else {
|
||||
this.sortActive = { key: sort.key, order: 'desc' }
|
||||
}
|
||||
this.$router.replace({
|
||||
name: this.$route.name,
|
||||
params: { libraryId: this.$route.params.libraryId, index: this.$route.params.index },
|
||||
query: { sort: `${this.sortActive.key},${this.sortActive.order}` }
|
||||
})
|
||||
this.updateRoute()
|
||||
this.reloadData(this.libraryId)
|
||||
},
|
||||
async loadInitialData (libraryId: number, pageToLoad: number = 0) {
|
||||
|
|
@ -230,7 +289,7 @@ export default Vue.extend({
|
|||
if (libraryId !== 0) {
|
||||
requestLibraryId = libraryId
|
||||
}
|
||||
return this.$komgaSeries.getSeries(requestLibraryId, pageRequest)
|
||||
return this.$komgaSeries.getSeries(requestLibraryId, pageRequest, undefined, this.filterStatus)
|
||||
},
|
||||
processPage (page: Page<SeriesDto>) {
|
||||
if (this.totalElements === null) {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ export default class KomgaSeriesService {
|
|||
this.http = http
|
||||
}
|
||||
|
||||
async getSeries (libraryId?: number, pageRequest?: PageRequest, search?: string): Promise<Page<SeriesDto>> {
|
||||
async getSeries (libraryId?: number, pageRequest?: PageRequest, search?: string, status?: string[]): Promise<Page<SeriesDto>> {
|
||||
try {
|
||||
const params = { ...pageRequest } as any
|
||||
if (libraryId) {
|
||||
|
|
@ -20,6 +20,9 @@ export default class KomgaSeriesService {
|
|||
if (search) {
|
||||
params.search = search
|
||||
}
|
||||
if (status) {
|
||||
params.status = status
|
||||
}
|
||||
return (await this.http.get(API_SERIES, {
|
||||
params: params,
|
||||
paramsSerializer: params => qs.stringify(params, { indices: false })
|
||||
|
|
|
|||
|
|
@ -16,3 +16,10 @@ export enum MediaStatus {
|
|||
Error = 'ERROR',
|
||||
Unsupported = 'UNSUPPORTED'
|
||||
}
|
||||
|
||||
export enum SeriesStatus {
|
||||
ENDED = 'ENDED',
|
||||
ONGOING = 'ONGOING',
|
||||
ABANDONED = 'ABANDONED',
|
||||
HIATUS = 'HIATUS'
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue