mirror of
https://github.com/gotson/komga.git
synced 2026-04-16 12:01:09 +02:00
feat(webui): series multi-selection and edition
Series cards can now display an edit button on hover, and can be multi-selectable inline edition enabled when browsing libraries and on dashboard multi-selection for edition enabled when browing libraries
This commit is contained in:
parent
4e0e409326
commit
cfce0768ba
7 changed files with 301 additions and 90 deletions
|
|
@ -1,34 +1,61 @@
|
||||||
<template>
|
<template>
|
||||||
<v-card :width="width"
|
<v-hover :disabled="!overlay">
|
||||||
:to="{name:'browse-series', params: {seriesId: series.id}}"
|
<template v-slot:default="{ hover }">
|
||||||
>
|
<v-card :width="width"
|
||||||
<v-img
|
:to="{name:'browse-series', params: {seriesId: series.id}}"
|
||||||
:src="thumbnailUrl"
|
|
||||||
lazy-src="../assets/cover.svg"
|
|
||||||
aspect-ratio="0.7071"
|
|
||||||
>
|
|
||||||
<span class="white--text pa-1 px-2 subtitle-2"
|
|
||||||
style="background: darkorange; position: absolute; right: 0"
|
|
||||||
>
|
>
|
||||||
{{ series.booksCount }}
|
<v-img
|
||||||
</span>
|
:src="thumbnailUrl"
|
||||||
</v-img>
|
lazy-src="../assets/cover.svg"
|
||||||
|
aspect-ratio="0.7071"
|
||||||
|
>
|
||||||
|
<span class="white--text pa-1 px-2 subtitle-2"
|
||||||
|
style="background: darkorange; position: absolute; right: 0"
|
||||||
|
>
|
||||||
|
{{ series.booksCount }}
|
||||||
|
</span>
|
||||||
|
<v-fade-transition>
|
||||||
|
<v-overlay
|
||||||
|
v-if="hover || selected"
|
||||||
|
absolute
|
||||||
|
:opacity="hover ? 0.3 : 0"
|
||||||
|
:class="`item-border${hover ? '-darken' : ''} overlay-full`"
|
||||||
|
>
|
||||||
|
<v-icon v-if="select"
|
||||||
|
:color="selected ? 'secondary' : ''"
|
||||||
|
style="position: absolute; top: 5px; left: 10px"
|
||||||
|
@click.prevent="selectItem"
|
||||||
|
>
|
||||||
|
{{ selected ? 'mdi-checkbox-marked-circle' : 'mdi-checkbox-blank-circle-outline' }}
|
||||||
|
</v-icon>
|
||||||
|
|
||||||
<v-card-subtitle class="pa-2 pb-1 text--primary"
|
<v-icon v-if="!selected && showEdit && edit"
|
||||||
v-line-clamp="2"
|
style="position: absolute; bottom: 10px; left: 10px"
|
||||||
style="word-break: normal !important; height: 4em"
|
@click.prevent="editItem"
|
||||||
:title="series.metadata.title"
|
>
|
||||||
>
|
mdi-pencil
|
||||||
{{ series.metadata.title }}
|
</v-icon>
|
||||||
</v-card-subtitle>
|
</v-overlay>
|
||||||
|
</v-fade-transition>
|
||||||
|
</v-img>
|
||||||
|
|
||||||
<v-card-text class="px-2"
|
<v-card-subtitle class="pa-2 pb-1 text--primary"
|
||||||
>
|
v-line-clamp="2"
|
||||||
<span v-if="series.booksCount === 1">{{ series.booksCount }} book</span>
|
style="word-break: normal !important; height: 4em"
|
||||||
<span v-else>{{ series.booksCount }} books</span>
|
:title="series.metadata.title"
|
||||||
</v-card-text>
|
>
|
||||||
|
{{ series.metadata.title }}
|
||||||
|
</v-card-subtitle>
|
||||||
|
|
||||||
</v-card>
|
<v-card-text class="px-2"
|
||||||
|
>
|
||||||
|
<span v-if="series.booksCount === 1">{{ series.booksCount }} book</span>
|
||||||
|
<span v-else>{{ series.booksCount }} books</span>
|
||||||
|
</v-card-text>
|
||||||
|
|
||||||
|
</v-card>
|
||||||
|
</template>
|
||||||
|
</v-hover>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
|
@ -46,15 +73,58 @@ export default Vue.extend({
|
||||||
type: [String, Number],
|
type: [String, Number],
|
||||||
required: false,
|
required: false,
|
||||||
default: 150
|
default: 150
|
||||||
|
},
|
||||||
|
selected: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
showEdit: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
type: Function,
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
edit: {
|
||||||
|
type: Function,
|
||||||
|
required: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
thumbnailUrl (): string {
|
thumbnailUrl (): string {
|
||||||
return seriesThumbnailUrl(this.series.id)
|
return seriesThumbnailUrl(this.series.id)
|
||||||
|
},
|
||||||
|
overlay (): boolean {
|
||||||
|
return this.edit !== undefined || this.select !== undefined
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
selectItem () {
|
||||||
|
if (this.select !== undefined) {
|
||||||
|
this.select()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
editItem () {
|
||||||
|
if (this.edit !== undefined) {
|
||||||
|
this.edit(this.series)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style>
|
||||||
|
.item-border {
|
||||||
|
border: 3px solid var(--v-secondary-base);
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-border-darken {
|
||||||
|
border: 3px solid var(--v-secondary-darken2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay-full .v-overlay__content {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
<v-btn icon @click="dialogCancel">
|
<v-btn icon @click="dialogCancel">
|
||||||
<v-icon>mdi-close</v-icon>
|
<v-icon>mdi-close</v-icon>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-toolbar-title>Edit {{ $_.get(series, 'metadata.title') }}</v-toolbar-title>
|
<v-toolbar-title>{{ dialogTitle }}</v-toolbar-title>
|
||||||
<v-spacer/>
|
<v-spacer/>
|
||||||
<v-toolbar-items>
|
<v-toolbar-items>
|
||||||
<v-btn text color="primary" @click="dialogConfirm">Save changes</v-btn>
|
<v-btn text color="primary" @click="dialogConfirm">Save changes</v-btn>
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
<v-card-title class="hidden-xs-only">
|
<v-card-title class="hidden-xs-only">
|
||||||
<v-icon class="mr-4">mdi-pencil</v-icon>
|
<v-icon class="mr-4">mdi-pencil</v-icon>
|
||||||
Edit {{ $_.get(series, 'metadata.title') }}
|
{{ dialogTitle }}
|
||||||
</v-card-title>
|
</v-card-title>
|
||||||
|
|
||||||
<v-tabs :vertical="$vuetify.breakpoint.smAndUp">
|
<v-tabs :vertical="$vuetify.breakpoint.smAndUp">
|
||||||
|
|
@ -34,7 +34,7 @@
|
||||||
<v-container fluid>
|
<v-container fluid>
|
||||||
|
|
||||||
<!-- Title -->
|
<!-- Title -->
|
||||||
<v-row>
|
<v-row v-if="!multiple">
|
||||||
<v-col cols="12">
|
<v-col cols="12">
|
||||||
<v-text-field v-model="form.title"
|
<v-text-field v-model="form.title"
|
||||||
label="Title"
|
label="Title"
|
||||||
|
|
@ -52,7 +52,7 @@
|
||||||
</v-row>
|
</v-row>
|
||||||
|
|
||||||
<!-- Sort Title -->
|
<!-- Sort Title -->
|
||||||
<v-row>
|
<v-row v-if="!multiple">
|
||||||
<v-col cols="12">
|
<v-col cols="12">
|
||||||
<v-text-field v-model="form.titleSort"
|
<v-text-field v-model="form.titleSort"
|
||||||
label="Sort Title"
|
label="Sort Title"
|
||||||
|
|
@ -130,7 +130,6 @@ export default Vue.extend({
|
||||||
modal: false,
|
modal: false,
|
||||||
snackbar: false,
|
snackbar: false,
|
||||||
snackText: '',
|
snackText: '',
|
||||||
seriesStatus: Object.keys(SeriesStatus).map(x => capitalize(x)),
|
|
||||||
form: {
|
form: {
|
||||||
status: '',
|
status: '',
|
||||||
statusLock: false,
|
statusLock: false,
|
||||||
|
|
@ -144,7 +143,7 @@ export default Vue.extend({
|
||||||
props: {
|
props: {
|
||||||
value: Boolean,
|
value: Boolean,
|
||||||
series: {
|
series: {
|
||||||
type: Object,
|
type: Array as () => SeriesDto[],
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -160,18 +159,34 @@ export default Vue.extend({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
libraries (): LibraryDto[] {
|
multiple (): boolean {
|
||||||
return this.$store.state.komgaLibraries.libraries
|
return this.series.length > 1
|
||||||
|
},
|
||||||
|
seriesStatus (): string[] {
|
||||||
|
return Object.keys(SeriesStatus).map(x => capitalize(x))
|
||||||
|
},
|
||||||
|
dialogTitle (): string {
|
||||||
|
return this.multiple
|
||||||
|
? `Edit ${this.series.length} series`
|
||||||
|
: `Edit ${this.$_.get(this.series[0], 'metadata.title')}`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
dialogReset (series: SeriesDto) {
|
dialogReset (series: SeriesDto[]) {
|
||||||
this.form.status = capitalize(series.metadata.status)
|
if (series.length === 0) return
|
||||||
this.form.statusLock = series.metadata.statusLock
|
if (series.length > 1) {
|
||||||
this.form.title = series.metadata.title
|
const status = this.$_.uniq(series.map(x => x.metadata.status))
|
||||||
this.form.titleLock = series.metadata.titleLock
|
this.form.status = status.length > 1 ? '' : capitalize(status[0])
|
||||||
this.form.titleSort = series.metadata.titleSort
|
const statusLock = this.$_.uniq(series.map(x => x.metadata.statusLock))
|
||||||
this.form.titleSortLock = series.metadata.titleSortLock
|
this.form.statusLock = statusLock.length > 1 ? false : statusLock[0]
|
||||||
|
} else {
|
||||||
|
this.form.status = capitalize(series[0].metadata.status)
|
||||||
|
this.form.statusLock = series[0].metadata.statusLock
|
||||||
|
this.form.title = series[0].metadata.title
|
||||||
|
this.form.titleLock = series[0].metadata.titleLock
|
||||||
|
this.form.titleSort = series[0].metadata.titleSort
|
||||||
|
this.form.titleSortLock = series[0].metadata.titleSortLock
|
||||||
|
}
|
||||||
},
|
},
|
||||||
dialogCancel () {
|
dialogCancel () {
|
||||||
this.$emit('input', false)
|
this.$emit('input', false)
|
||||||
|
|
@ -186,21 +201,34 @@ export default Vue.extend({
|
||||||
this.snackbar = true
|
this.snackbar = true
|
||||||
},
|
},
|
||||||
async editSeries () {
|
async editSeries () {
|
||||||
try {
|
const updated = [] as SeriesDto[]
|
||||||
const metadata = {
|
for (const s of this.series) {
|
||||||
status: this.form.status.toUpperCase(),
|
try {
|
||||||
statusLock: this.form.statusLock,
|
if (this.form.status === '') {
|
||||||
title: this.form.title,
|
return
|
||||||
titleLock: this.form.titleLock,
|
}
|
||||||
titleSort: this.form.titleSort,
|
const metadata = {
|
||||||
titleSortLock: this.form.titleSortLock
|
status: this.form.status.toUpperCase(),
|
||||||
} as SeriesMetadataUpdateDto
|
statusLock: this.form.statusLock
|
||||||
|
} as SeriesMetadataUpdateDto
|
||||||
|
|
||||||
const updatedSeries = await this.$komgaSeries.updateMetadata(this.series.id, metadata)
|
if (!this.multiple) {
|
||||||
this.$emit('update:series', updatedSeries)
|
this.$_.merge(metadata, {
|
||||||
} catch (e) {
|
title: this.form.title,
|
||||||
this.showSnack(e.message)
|
titleLock: this.form.titleLock,
|
||||||
|
titleSort: this.form.titleSort,
|
||||||
|
titleSortLock: this.form.titleSortLock
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedSeries = await this.$komgaSeries.updateMetadata(s.id, metadata)
|
||||||
|
updated.push(updatedSeries)
|
||||||
|
} catch (e) {
|
||||||
|
this.showSnack(e.message)
|
||||||
|
updated.push(s)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
this.$emit('update:series', updated)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<v-toolbar flat
|
<v-toolbar flat
|
||||||
color="grey lighten-4"
|
:color="color"
|
||||||
class="sticky-bar"
|
class="sticky-bar"
|
||||||
:style="barStyle"
|
:style="barStyle"
|
||||||
|
:elevation="elevation"
|
||||||
>
|
>
|
||||||
<slot/>
|
<slot/>
|
||||||
</v-toolbar>
|
</v-toolbar>
|
||||||
|
|
@ -15,12 +16,22 @@ export default Vue.extend({
|
||||||
name: 'ToolbarSticky',
|
name: 'ToolbarSticky',
|
||||||
computed: {
|
computed: {
|
||||||
barStyle (): any {
|
barStyle (): any {
|
||||||
if (this.$vuetify.breakpoint.name === 'xs') {
|
if (['xs', 'sm'].includes(this.$vuetify.breakpoint.name)) {
|
||||||
return { 'top': '56px' }
|
return { 'top': '56px' }
|
||||||
} else {
|
} else {
|
||||||
return { 'top': '64px' }
|
return { 'top': '64px' }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
elevation: {
|
||||||
|
type: Number,
|
||||||
|
default: undefined
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
type: String,
|
||||||
|
default: 'grey lighten-4'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,9 @@ export default new Vuetify({
|
||||||
iconfont: 'mdi'
|
iconfont: 'mdi'
|
||||||
},
|
},
|
||||||
theme: {
|
theme: {
|
||||||
|
options: {
|
||||||
|
customProperties: true
|
||||||
|
},
|
||||||
themes: {
|
themes: {
|
||||||
light: {
|
light: {
|
||||||
primary: '#005ed3',
|
primary: '#005ed3',
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<toolbar-sticky>
|
<toolbar-sticky v-if="selected.length === 0">
|
||||||
<!-- Action menu -->
|
<!-- Action menu -->
|
||||||
<library-actions-menu v-if="library"
|
<library-actions-menu v-if="library"
|
||||||
:library="library"/>
|
:library="library"/>
|
||||||
|
|
@ -44,36 +44,69 @@
|
||||||
/>
|
/>
|
||||||
</toolbar-sticky>
|
</toolbar-sticky>
|
||||||
|
|
||||||
<v-container fluid class="px-6">
|
<v-scroll-y-transition hide-on-leave>
|
||||||
<v-row justify="start" ref="content" v-resize="updateCardWidth" v-if="totalElements !== 0">
|
<toolbar-sticky v-if="selected.length > 0" :elevation="5" color="white">
|
||||||
|
<v-btn icon @click="selected=[]">
|
||||||
|
<v-icon>mdi-close</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
<v-toolbar-title>
|
||||||
|
<span>{{ selected.length }} selected</span>
|
||||||
|
</v-toolbar-title>
|
||||||
|
|
||||||
<v-skeleton-loader v-for="(s, i) in series"
|
<v-spacer/>
|
||||||
:key="i"
|
|
||||||
|
<v-btn icon @click="dialogEdit = true" v-if="isAdmin">
|
||||||
|
<v-icon>mdi-pencil</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</toolbar-sticky>
|
||||||
|
</v-scroll-y-transition>
|
||||||
|
|
||||||
|
<edit-series-dialog v-model="dialogEdit"
|
||||||
|
:series.sync="selectedSeries"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<edit-series-dialog v-model="dialogEditSingle"
|
||||||
|
:series.sync="editSeriesSingle"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<v-item-group multiple v-model="selected">
|
||||||
|
<v-container fluid class="px-6">
|
||||||
|
<v-row justify="start" ref="content" v-resize="updateCardWidth" v-if="totalElements !== 0">
|
||||||
|
<v-skeleton-loader v-for="(s, i) in series"
|
||||||
|
:key="i"
|
||||||
|
:width="cardWidth"
|
||||||
|
:height="cardWidth / .7071 + 94"
|
||||||
|
justify-self="start"
|
||||||
|
:loading="s === null"
|
||||||
|
type="card, text"
|
||||||
|
class="ma-3 mx-2"
|
||||||
|
v-intersect="onElementIntersect"
|
||||||
|
:data-index="i"
|
||||||
|
>
|
||||||
|
<v-item v-slot:default="{ active, toggle }" :value="$_.get(s, 'id', 0)">
|
||||||
|
<card-series :series="s"
|
||||||
:width="cardWidth"
|
:width="cardWidth"
|
||||||
:height="cardWidth / .7071 + 94"
|
:selected="active"
|
||||||
justify-self="start"
|
:select="toggle"
|
||||||
:loading="s === null"
|
:showEdit="selected.length === 0"
|
||||||
type="card, text"
|
:edit="singleEdit"
|
||||||
class="ma-3 mx-2"
|
/>
|
||||||
v-intersect="onElementIntersect"
|
</v-item>
|
||||||
:data-index="i"
|
</v-skeleton-loader>
|
||||||
>
|
</v-row>
|
||||||
<card-series :series="s" :width="cardWidth"/>
|
|
||||||
</v-skeleton-loader>
|
|
||||||
|
|
||||||
</v-row>
|
<!-- Empty state if filter returns no books -->
|
||||||
|
<v-row justify="center" v-else>
|
||||||
<!-- Empty state if filter returns no books -->
|
<empty-state title="The active filter has no matches"
|
||||||
<v-row justify="center" v-else>
|
sub-title="Use the menu above to change the active filter"
|
||||||
<empty-state title="The active filter has no matches"
|
icon="mdi-book-multiple"
|
||||||
sub-title="Use the menu above to change the active filter"
|
icon-color="secondary"
|
||||||
icon="mdi-book-multiple"
|
>
|
||||||
icon-color="secondary"
|
<v-btn @click="filterStatus = []">Clear filter</v-btn>
|
||||||
>
|
</empty-state>
|
||||||
<v-btn @click="filterStatus = []">Clear filter</v-btn>
|
</v-row>
|
||||||
</empty-state>
|
</v-container>
|
||||||
</v-row>
|
</v-item-group>
|
||||||
</v-container>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -81,6 +114,7 @@
|
||||||
import Badge from '@/components/Badge.vue'
|
import Badge from '@/components/Badge.vue'
|
||||||
import CardSeries from '@/components/CardSeries.vue'
|
import CardSeries from '@/components/CardSeries.vue'
|
||||||
import EmptyState from '@/components/EmptyState.vue'
|
import EmptyState from '@/components/EmptyState.vue'
|
||||||
|
import EditSeriesDialog from '@/components/EditSeriesDialog.vue'
|
||||||
import LibraryActionsMenu from '@/components/LibraryActionsMenu.vue'
|
import LibraryActionsMenu from '@/components/LibraryActionsMenu.vue'
|
||||||
import SortMenuButton from '@/components/SortMenuButton.vue'
|
import SortMenuButton from '@/components/SortMenuButton.vue'
|
||||||
import ToolbarSticky from '@/components/ToolbarSticky.vue'
|
import ToolbarSticky from '@/components/ToolbarSticky.vue'
|
||||||
|
|
@ -92,11 +126,13 @@ import mixins from 'vue-typed-mixins'
|
||||||
|
|
||||||
export default mixins(VisibleElements).extend({
|
export default mixins(VisibleElements).extend({
|
||||||
name: 'BrowseLibraries',
|
name: 'BrowseLibraries',
|
||||||
components: { LibraryActionsMenu, CardSeries, EmptyState, ToolbarSticky, SortMenuButton, Badge },
|
components: { LibraryActionsMenu, CardSeries, EmptyState, ToolbarSticky, SortMenuButton, Badge, EditSeriesDialog },
|
||||||
data: () => {
|
data: () => {
|
||||||
return {
|
return {
|
||||||
library: undefined as LibraryDto | undefined,
|
library: undefined as LibraryDto | undefined,
|
||||||
series: [] as SeriesDto[],
|
series: [] as SeriesDto[],
|
||||||
|
selectedSeries: [] as SeriesDto[],
|
||||||
|
editSeriesSingle: [] as SeriesDto[],
|
||||||
pagesState: [] as LoadState[],
|
pagesState: [] as LoadState[],
|
||||||
pageSize: 20,
|
pageSize: 20,
|
||||||
totalElements: null as number | null,
|
totalElements: null as number | null,
|
||||||
|
|
@ -111,7 +147,10 @@ export default mixins(VisibleElements).extend({
|
||||||
SeriesStatus,
|
SeriesStatus,
|
||||||
cardWidth: 150,
|
cardWidth: 150,
|
||||||
sortUnwatch: null as any,
|
sortUnwatch: null as any,
|
||||||
filterUnwatch: null as any
|
filterUnwatch: null as any,
|
||||||
|
selected: [],
|
||||||
|
dialogEdit: false,
|
||||||
|
dialogEditSingle: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
|
|
@ -135,6 +174,26 @@ export default mixins(VisibleElements).extend({
|
||||||
if (this.$route.params.index !== index) {
|
if (this.$route.params.index !== index) {
|
||||||
this.updateRoute(index)
|
this.updateRoute(index)
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
selected (val: number[]) {
|
||||||
|
this.selectedSeries = val.map(id => this.series.find(s => s.id === id))
|
||||||
|
.filter(x => x !== undefined) as SeriesDto[]
|
||||||
|
},
|
||||||
|
selectedSeries (val: SeriesDto[]) {
|
||||||
|
val.forEach(s => {
|
||||||
|
const index = this.series.findIndex(x => x.id === s.id)
|
||||||
|
if (index !== -1) {
|
||||||
|
this.series[index] = s
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
editSeriesSingle (val: SeriesDto[]) {
|
||||||
|
val.forEach(s => {
|
||||||
|
const index = this.series.findIndex(x => x.id === s.id)
|
||||||
|
if (index !== -1) {
|
||||||
|
this.series[index] = s
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async created () {
|
async created () {
|
||||||
|
|
@ -178,6 +237,11 @@ export default mixins(VisibleElements).extend({
|
||||||
|
|
||||||
next()
|
next()
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
isAdmin (): boolean {
|
||||||
|
return this.$store.getters.meAdmin
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
setWatches () {
|
setWatches () {
|
||||||
this.sortUnwatch = this.$watch('sortActive', this.updateRouteAndReload)
|
this.sortUnwatch = this.$watch('sortActive', this.updateRouteAndReload)
|
||||||
|
|
@ -255,6 +319,10 @@ export default mixins(VisibleElements).extend({
|
||||||
} else {
|
} else {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
singleEdit (series: SeriesDto) {
|
||||||
|
this.editSeriesSingle = [series]
|
||||||
|
this.dialogEditSingle = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,7 @@
|
||||||
</v-container>
|
</v-container>
|
||||||
|
|
||||||
<edit-series-dialog v-model="dialogEdit"
|
<edit-series-dialog v-model="dialogEdit"
|
||||||
:series.sync="series"/>
|
:series.sync="seriesArray"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -136,6 +136,14 @@ export default mixins(VisibleElements).extend({
|
||||||
},
|
},
|
||||||
thumbnailUrl (): string {
|
thumbnailUrl (): string {
|
||||||
return seriesThumbnailUrl(this.seriesId)
|
return seriesThumbnailUrl(this.seriesId)
|
||||||
|
},
|
||||||
|
seriesArray: {
|
||||||
|
get (): SeriesDto[] {
|
||||||
|
return [this.series]
|
||||||
|
},
|
||||||
|
set (val: SeriesDto[]) {
|
||||||
|
this.series = val[0]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="ma-3">
|
<div class="ma-3">
|
||||||
|
|
||||||
|
<edit-series-dialog v-model="dialogEditSingle"
|
||||||
|
:series.sync="editSeriesSingle"
|
||||||
|
/>
|
||||||
|
|
||||||
<horizontal-scroller>
|
<horizontal-scroller>
|
||||||
<template v-slot:prepend>
|
<template v-slot:prepend>
|
||||||
<div class="title">Recently Added Series</div>
|
<div class="title">Recently Added Series</div>
|
||||||
|
|
@ -18,6 +22,7 @@
|
||||||
<card-series v-else
|
<card-series v-else
|
||||||
:series="s"
|
:series="s"
|
||||||
class="ma-2 card"
|
class="ma-2 card"
|
||||||
|
:edit="singleEdit"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -42,6 +47,7 @@
|
||||||
<card-series v-else
|
<card-series v-else
|
||||||
:series="s"
|
:series="s"
|
||||||
class="ma-2 card"
|
class="ma-2 card"
|
||||||
|
:edit="singleEdit"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -80,17 +86,20 @@ import CardBook from '@/components/CardBook.vue'
|
||||||
import CardSeries from '@/components/CardSeries.vue'
|
import CardSeries from '@/components/CardSeries.vue'
|
||||||
import HorizontalScroller from '@/components/HorizontalScroller.vue'
|
import HorizontalScroller from '@/components/HorizontalScroller.vue'
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
|
import EditSeriesDialog from '@/components/EditSeriesDialog.vue'
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
name: 'Dashboard',
|
name: 'Dashboard',
|
||||||
components: { CardSeries, CardBook, HorizontalScroller },
|
components: { CardSeries, CardBook, HorizontalScroller, EditSeriesDialog },
|
||||||
data: () => {
|
data: () => {
|
||||||
const pageSize = 20
|
const pageSize = 20
|
||||||
return {
|
return {
|
||||||
newSeries: Array(pageSize).fill(null) as SeriesDto[],
|
newSeries: Array(pageSize).fill(null) as SeriesDto[],
|
||||||
updatedSeries: Array(pageSize).fill(null) as SeriesDto[],
|
updatedSeries: Array(pageSize).fill(null) as SeriesDto[],
|
||||||
books: Array(pageSize).fill(null) as BookDto[],
|
books: Array(pageSize).fill(null) as BookDto[],
|
||||||
pageSize: pageSize
|
pageSize: pageSize,
|
||||||
|
editSeriesSingle: [] as SeriesDto[],
|
||||||
|
dialogEditSingle: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
|
|
@ -102,6 +111,16 @@ export default Vue.extend({
|
||||||
this.loadLatestBooks()
|
this.loadLatestBooks()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
editSeriesSingle (val: SeriesDto[]) {
|
||||||
|
val.forEach(s => {
|
||||||
|
const index = this.newSeries.findIndex(x => x.id === s.id)
|
||||||
|
if (index !== -1) {
|
||||||
|
this.newSeries[index] = s
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async loadNewSeries () {
|
async loadNewSeries () {
|
||||||
this.newSeries = (await this.$komgaSeries.getNewSeries()).content
|
this.newSeries = (await this.$komgaSeries.getNewSeries()).content
|
||||||
|
|
@ -116,6 +135,10 @@ export default Vue.extend({
|
||||||
} as PageRequest
|
} as PageRequest
|
||||||
|
|
||||||
this.books = (await this.$komgaBooks.getBooks(undefined, pageRequest)).content
|
this.books = (await this.$komgaBooks.getBooks(undefined, pageRequest)).content
|
||||||
|
},
|
||||||
|
singleEdit (series: SeriesDto) {
|
||||||
|
this.editSeriesSingle = [series]
|
||||||
|
this.dialogEditSingle = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue