feat(webui)): edit series metadata

This commit is contained in:
Gauthier Roebroeck 2020-01-30 11:59:52 +08:00
parent 8b02471be1
commit 5f0ccc5bfc
5 changed files with 190 additions and 4 deletions

View file

@ -0,0 +1,150 @@
<template>
<div>
<v-dialog v-model="modal"
:fullscreen="$vuetify.breakpoint.xsOnly"
max-width="800"
>
<v-card>
<v-toolbar class="hidden-sm-and-up">
<v-btn icon @click="dialogCancel">
<v-icon>mdi-close</v-icon>
</v-btn>
<v-toolbar-title>Edit {{ series.name }}</v-toolbar-title>
<v-spacer/>
<v-toolbar-items>
<v-btn text color="primary" @click="dialogConfirm">Save changes</v-btn>
</v-toolbar-items>
</v-toolbar>
<v-card-title class="hidden-xs-only">
<v-icon class="mr-4">mdi-pencil</v-icon>
Edit {{ series.name }}
</v-card-title>
<v-tabs :vertical="$vuetify.breakpoint.smAndUp">
<v-tab class="justify-start">
<v-icon left class="hidden-xs-only">mdi-format-align-center</v-icon>
General
</v-tab>
<!-- General -->
<v-tab-item>
<v-card flat>
<form novalidate>
<v-container fluid>
<v-row>
<v-col cols="auto">
<v-select
:items="seriesStatus"
v-model="form.status"
label="Status"
/>
</v-col>
</v-row>
</v-container>
</form>
</v-card>
</v-tab-item>
</v-tabs>
<v-card-actions class="hidden-xs-only">
<v-spacer/>
<v-btn text @click="dialogCancel">Cancel</v-btn>
<v-btn text class="primary--text" @click="dialogConfirm">Save changes</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<v-snackbar
v-model="snackbar"
bottom
color="error"
>
{{ snackText }}
<v-btn
text
@click="snackbar = false"
>
Close
</v-btn>
</v-snackbar>
</div>
</template>
<script lang="ts">
import { capitalize } from '@/functions/text'
import { SeriesStatus } from '@/types/common'
import Vue from 'vue'
export default Vue.extend({
name: 'EditSeriesDialog',
data: () => {
return {
modal: false,
snackbar: false,
snackText: '',
seriesStatus: Object.keys(SeriesStatus).map(x => capitalize(x)),
form: {
status: ''
}
}
},
props: {
value: Boolean,
series: {
type: Object,
required: true
}
},
watch: {
value (val) {
this.modal = val
},
modal (val) {
!val && this.dialogCancel()
},
series (val) {
this.dialogReset(val)
}
},
computed: {
libraries (): LibraryDto[] {
return this.$store.state.komgaLibraries.libraries
}
},
methods: {
dialogReset (series: SeriesDto) {
this.form.status = capitalize(series.metadata.status)
},
dialogCancel () {
this.$emit('input', false)
this.dialogReset(this.series)
},
dialogConfirm () {
this.editSeries()
this.$emit('input', false)
this.dialogReset(this.series)
},
showSnack (message: string) {
this.snackText = message
this.snackbar = true
},
async editSeries () {
try {
const metadata = {
status: this.form.status.toUpperCase()
} as SeriesMetadataUpdateDto
await this.$komgaSeries.updateMetadata(this.series.id, metadata)
} catch (e) {
this.showSnack(e.message)
}
}
}
})
</script>
<style scoped>
</style>

View file

@ -0,0 +1,3 @@
export function capitalize (s: string) {
return `${s[0].toUpperCase()}${s.slice(1).toLowerCase()}`
}

View file

@ -5,7 +5,7 @@ const qs = require('qs')
const API_SERIES = '/api/v1/series'
export default class KomgaSeriesService {
private http: AxiosInstance;
private http: AxiosInstance
constructor (http: AxiosInstance) {
this.http = http
@ -104,4 +104,16 @@ export default class KomgaSeriesService {
throw new Error(msg)
}
}
async updateMetadata (seriesId: number, metadata: SeriesMetadataUpdateDto) {
try {
await this.http.patch(`${API_SERIES}/${seriesId}/metadata`, metadata)
} catch (e) {
let msg = `An error occurred while trying to update series metadata`
if (e.response.data.message) {
msg += `: ${e.response.data.message}`
}
throw new Error(msg)
}
}
}

View file

@ -13,3 +13,7 @@ interface SeriesMetadata {
created: string,
lastModified: string
}
interface SeriesMetadataUpdateDto {
status?: string
}

View file

@ -30,6 +30,10 @@
<v-spacer/>
<v-btn icon @click="dialogEdit = true" v-if="isAdmin">
<v-icon>mdi-pencil</v-icon>
</v-btn>
<!-- Sort menu -->
<sort-menu-button :sort-default="sortDefault"
:sort-options="sortOptions"
@ -83,12 +87,16 @@
</v-row>
</v-container>
<edit-series-dialog v-model="dialogEdit"
:series="series"/>
</div>
</template>
<script lang="ts">
import Badge from '@/components/Badge.vue'
import CardBook from '@/components/CardBook.vue'
import EditSeriesDialog from '@/components/EditSeriesDialog.vue'
import SortMenuButton from '@/components/SortMenuButton.vue'
import ToolbarSticky from '@/components/ToolbarSticky.vue'
import { computeCardWidth } from '@/functions/grid-utilities'
@ -100,7 +108,7 @@ import mixins from 'vue-typed-mixins'
export default mixins(VisibleElements).extend({
name: 'BrowseSeries',
components: { CardBook, ToolbarSticky, SortMenuButton, Badge },
components: { CardBook, ToolbarSticky, SortMenuButton, Badge, EditSeriesDialog },
data: () => {
return {
series: {} as SeriesDto,
@ -114,7 +122,8 @@ export default mixins(VisibleElements).extend({
}] as SortOption[],
sortActive: {} as SortActive,
sortDefault: { key: 'number', order: 'asc' } as SortActive,
cardWidth: 150
cardWidth: 150,
dialogEdit: false
}
},
computed: {
@ -153,10 +162,15 @@ export default mixins(VisibleElements).extend({
if (this.$route.params.index !== index) {
this.updateRoute(index)
}
},
dialogEdit (val) {
if (!val) {
this.loadSeries()
}
}
},
async created () {
this.series = await this.$komgaSeries.getOneSeries(this.seriesId)
this.loadSeries()
},
mounted () {
// fill books skeletons if an index is provided, so scroll position can be restored
@ -179,6 +193,9 @@ export default mixins(VisibleElements).extend({
next()
},
methods: {
async loadSeries () {
this.series = await this.$komgaSeries.getOneSeries(this.seriesId)
},
updateCardWidth () {
const content = this.$refs.content as HTMLElement
this.cardWidth = computeCardWidth(content.clientWidth, this.$vuetify.breakpoint.name)