mirror of
https://github.com/gotson/komga.git
synced 2025-12-22 00:13:30 +01:00
feat(webui)): edit series metadata
This commit is contained in:
parent
8b02471be1
commit
5f0ccc5bfc
5 changed files with 190 additions and 4 deletions
150
komga-webui/src/components/EditSeriesDialog.vue
Normal file
150
komga-webui/src/components/EditSeriesDialog.vue
Normal 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>
|
||||
3
komga-webui/src/functions/text.ts
Normal file
3
komga-webui/src/functions/text.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
export function capitalize (s: string) {
|
||||
return `${s[0].toUpperCase()}${s.slice(1).toLowerCase()}`
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,3 +13,7 @@ interface SeriesMetadata {
|
|||
created: string,
|
||||
lastModified: string
|
||||
}
|
||||
|
||||
interface SeriesMetadataUpdateDto {
|
||||
status?: string
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in a new issue