Skip to content

Commit

Permalink
feat(webui): collections management
Browse files Browse the repository at this point in the history
closes gotson#30
  • Loading branch information
gotson committed Jun 19, 2020
1 parent c2f9403 commit 2f8255a
Show file tree
Hide file tree
Showing 24 changed files with 1,440 additions and 82 deletions.
22 changes: 22 additions & 0 deletions komga-webui/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions komga-webui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"vue-moment": "^4.1.0",
"vue-router": "^3.1.5",
"vue-typed-mixins": "^0.2.0",
"vuedraggable": "^2.23.2",
"vuelidate": "^0.7.5",
"vuetify": "^2.2.14",
"vuex": "^3.1.2",
Expand All @@ -31,6 +32,7 @@
"@mdi/font": "^4.9.95",
"@types/jest": "^25.1.3",
"@types/lodash": "^4.14.149",
"@types/vuedraggable": "^2.23.1",
"@types/vuelidate": "^0.7.10",
"@vue/cli-plugin-babel": "^4.2.2",
"@vue/cli-plugin-eslint": "^4.2.2",
Expand Down
64 changes: 64 additions & 0 deletions komga-webui/src/components/CollectionActionsMenu.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<template>
<div>
<v-menu offset-y v-if="isAdmin">
<template v-slot:activator="{ on }">
<v-btn icon v-on="on" @click.prevent="">
<v-icon>mdi-dots-vertical</v-icon>
</v-btn>
</template>
<v-list>
<v-list-item @click="promptDeleteCollection"
class="list-warning">
<v-list-item-title>Delete</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>

<collection-delete-dialog v-model="modalDeleteCollection"
:collection="collection"
@deleted="afterDelete"
/>
</div>
</template>
<script lang="ts">
import CollectionDeleteDialog from '@/components/CollectionDeleteDialog.vue'
import Vue from 'vue'
export default Vue.extend({
name: 'CollectionActionsMenu',
components: { CollectionDeleteDialog },
data: function () {
return {
modalDeleteCollection: false,
}
},
props: {
collection: {
type: Object,
required: true,
},
},
computed: {
isAdmin (): boolean {
return this.$store.getters.meAdmin
},
},
methods: {
promptDeleteCollection () {
this.modalDeleteCollection = true
},
afterDelete () {
this.$emit('deleted', true)
},
},
})
</script>
<style scoped>
.list-warning:hover {
background: #F44336;
}
.list-warning:hover .v-list-item__title {
color: white;
}
</style>
174 changes: 174 additions & 0 deletions komga-webui/src/components/CollectionAddToDialog.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
<template>
<div>
<v-dialog v-model="modal"
max-width="450"
scrollable
>
<v-card>
<v-card-title>Add to collection</v-card-title>
<v-btn icon absolute top right @click="dialogClose">
<v-icon>mdi-close</v-icon>
</v-btn>

<v-divider/>

<v-card-text style="height: 50%">
<v-container fluid>

<v-row align="center">
<v-col>
<v-text-field
v-model="newCollection"
label="Create new collection"
@keydown.enter="create"
:error-messages="duplicate"
/>
</v-col>
<v-col cols="auto">
<v-btn
color="primary"
@click="create"
:disabled="newCollection.length === 0 || duplicate !== ''"
>Create
</v-btn>
</v-col>
</v-row>

<v-divider/>

<v-row v-if="collections.length !== 0">
<v-col>
<v-list elevation="5">
<div v-for="(c, index) in collections"
:key="index"
>
<v-list-item @click="addTo(c)"
two-line
>
<v-list-item-content>
<v-list-item-title>{{ c.name }}</v-list-item-title>
<v-list-item-subtitle>{{ c.seriesIds.length }} series</v-list-item-subtitle>
</v-list-item-content>
</v-list-item>
<v-divider v-if="index !== collections.length-1"/>
</div>
</v-list>
</v-col>
</v-row>

</v-container>
</v-card-text>

</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 Vue from 'vue'
export default Vue.extend({
name: 'CollectionAddToDialog',
data: () => {
return {
confirmDelete: false,
snackbar: false,
snackText: '',
modal: false,
collections: [] as CollectionDto[],
selectedCollection: null,
newCollection: '',
}
},
props: {
value: Boolean,
series: {
type: [Object as () => SeriesDto, Array as () => SeriesDto[]],
required: true,
},
},
watch: {
async value (val) {
this.modal = val
if (val) {
this.newCollection = ''
this.collections = await this.$komgaCollections.getCollections()
}
},
modal (val) {
!val && this.dialogClose()
},
},
async mounted () {
},
computed: {
seriesIds (): number[] {
if (Array.isArray(this.series)) return this.series.map(s => s.id)
else return [this.series.id]
},
duplicate (): string {
if (this.newCollection !== '' && this.collections.some(e => e.name === this.newCollection)) {
return 'A collection with this name already exists'
} else return ''
},
},
methods: {
dialogClose () {
this.$emit('input', false)
},
showSnack (message: string) {
this.snackText = message
this.snackbar = true
},
async addTo (collection: CollectionDto) {
const seriesIds = this.$_.uniq(collection.seriesIds.concat(this.seriesIds))
const toUpdate = {
seriesIds: seriesIds,
} as CollectionUpdateDto
try {
await this.$komgaCollections.patchCollection(collection.id, toUpdate)
this.$emit('added', true)
this.dialogClose()
} catch (e) {
this.showSnack(e.message)
}
},
async create () {
const toCreate = {
name: this.newCollection,
ordered: false,
seriesIds: this.seriesIds,
} as CollectionCreationDto
try {
await this.$komgaCollections.postCollection(toCreate)
this.$emit('added', true)
this.dialogClose()
} catch (e) {
this.showSnack(e.message)
}
},
},
})
</script>

<style scoped>
</style>

0 comments on commit 2f8255a

Please sign in to comment.