add ability to update image info
This commit is contained in:
parent
6474c2fd8c
commit
92ed17efa5
12 changed files with 244 additions and 14 deletions
132
src/frontend/components/EditImageDialog.vue
Normal file
132
src/frontend/components/EditImageDialog.vue
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
<template>
|
||||
<div class="overlay edit-image-dialog" @click="$emit('bgclick')">
|
||||
<div class="overlay-content" @click.stop="">
|
||||
|
||||
<div class="area-image">
|
||||
<div class="has-image">
|
||||
<responsive-image :src="image.url" :title="image.title" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="area-settings">
|
||||
<table>
|
||||
<tr>
|
||||
<td><label>Title</label></td>
|
||||
<td><input type="text" v-model="title" placeholder="Flower by @artist" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="hint">Feel free to leave a credit to the artist/photographer in the title :)</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- TODO: autocomplete category -->
|
||||
<td><label>Category</label></td>
|
||||
<td><input type="text" v-model="category" placeholder="Plants" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="area-buttons">
|
||||
<button class="btn" @click="saveImage">🖼️ Save image</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType } from 'vue'
|
||||
import { Image } from '../../common/GameCommon'
|
||||
|
||||
import ResponsiveImage from './ResponsiveImage.vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'edit-image-dialog',
|
||||
components: {
|
||||
ResponsiveImage,
|
||||
},
|
||||
props: {
|
||||
image: {
|
||||
type: Object as PropType<Image>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
emits: {
|
||||
bgclick: null,
|
||||
saveClick: null,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
title: '',
|
||||
category: '',
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.title = this.image.title
|
||||
this.category = this.image.categories.length > 0 ? this.image.categories[0].title : ''
|
||||
},
|
||||
methods: {
|
||||
saveImage () {
|
||||
this.$emit('saveClick', {
|
||||
id: this.image.id,
|
||||
title: this.title,
|
||||
category: this.category,
|
||||
})
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
// TODO: scoped vs .edit-image-dialog
|
||||
<style>
|
||||
.edit-image-dialog .overlay-content {
|
||||
display: grid;
|
||||
grid-template-columns: auto 450px;
|
||||
grid-template-rows: auto;
|
||||
grid-template-areas:
|
||||
"image settings"
|
||||
"image buttons";
|
||||
height: 90%;
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
.edit-image-dialog .area-image {
|
||||
grid-area: image;
|
||||
margin: 20px;
|
||||
}
|
||||
.edit-image-dialog .area-image.no-image {
|
||||
align-content: center;
|
||||
display: grid;
|
||||
text-align: center;
|
||||
border: dashed 6px;
|
||||
position: relative;
|
||||
}
|
||||
.edit-image-dialog .area-image .has-image {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.edit-image-dialog .area-image .has-image .remove {
|
||||
position: absolute;
|
||||
top: .5em;
|
||||
left: .5em;
|
||||
}
|
||||
|
||||
.edit-image-dialog .area-settings {
|
||||
grid-area: settings;
|
||||
}
|
||||
|
||||
.edit-image-dialog .area-settings table input[type="text"] {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.edit-image-dialog .area-buttons {
|
||||
align-self: end;
|
||||
grid-area: buttons;
|
||||
}
|
||||
.edit-image-dialog .area-buttons button {
|
||||
width: 100%;
|
||||
margin-top: .5em;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div>
|
||||
<image-teaser v-for="(i,idx) in images" :image="i" @click="imageClicked(i)" :key="idx" />
|
||||
<image-teaser v-for="(i,idx) in images" :image="i" @click="imageClicked(i)" @editClick="imageEditClicked(i)" :key="idx" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
|
|
@ -22,11 +22,15 @@ export default defineComponent({
|
|||
},
|
||||
emits: {
|
||||
imageClicked: null,
|
||||
imageEditClicked: null,
|
||||
},
|
||||
methods: {
|
||||
imageClicked (image: Image) {
|
||||
this.$emit('imageClicked', image)
|
||||
},
|
||||
imageEditClicked (image: Image) {
|
||||
this.$emit('imageEditClicked', image)
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,10 @@
|
|||
<template>
|
||||
<div class="imageteaser" :style="style" @click="onClick"></div>
|
||||
<div
|
||||
class="imageteaser"
|
||||
:style="style"
|
||||
@click="onClick">
|
||||
<div class="btn edit" @click.stop="onEditClick">✏️</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
|
|
@ -20,10 +25,22 @@ export default defineComponent({
|
|||
}
|
||||
},
|
||||
},
|
||||
emits: {
|
||||
click: null,
|
||||
editClick: null,
|
||||
},
|
||||
methods: {
|
||||
onClick() {
|
||||
this.$emit('click')
|
||||
},
|
||||
onEditClick() {
|
||||
this.$emit('editClick')
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
<style type="css">
|
||||
.imageteaser { position: relative; }
|
||||
.imageteaser .edit { display: none; position: absolute; }
|
||||
.imageteaser:hover .edit { display: inline-block; }
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -30,8 +30,9 @@ in jigsawpuzzles.io
|
|||
</select>
|
||||
</label>
|
||||
</div>
|
||||
<image-library :images="images" @imageClicked="imageClicked" />
|
||||
<image-library :images="images" @imageClicked="onImageClicked" @imageEditClicked="onImageEditClicked" />
|
||||
<new-image-dialog v-if="dialog==='new-image'" @bgclick="dialog=''" @postToGalleryClick="postToGalleryClick" @setupGameClick="setupGameClick" />
|
||||
<edit-image-dialog v-if="dialog==='edit-image'" @bgclick="dialog=''" @saveClick="onSaveImageClick" :image="image" />
|
||||
<new-game-dialog v-if="image && dialog==='new-game'" @bgclick="dialog=''" @newGame="onNewGame" :image="image" />
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -41,6 +42,7 @@ import { defineComponent } from 'vue'
|
|||
|
||||
import ImageLibrary from './../components/ImageLibrary.vue'
|
||||
import NewImageDialog from './../components/NewImageDialog.vue'
|
||||
import EditImageDialog from './../components/EditImageDialog.vue'
|
||||
import NewGameDialog from './../components/NewGameDialog.vue'
|
||||
import { GameSettings, Image } from '../../common/GameCommon'
|
||||
import Util from '../../common/Util'
|
||||
|
|
@ -49,6 +51,7 @@ export default defineComponent({
|
|||
components: {
|
||||
ImageLibrary,
|
||||
NewImageDialog,
|
||||
EditImageDialog,
|
||||
NewGameDialog,
|
||||
},
|
||||
data() {
|
||||
|
|
@ -86,22 +89,46 @@ export default defineComponent({
|
|||
async filtersChanged () {
|
||||
await this.loadImages()
|
||||
},
|
||||
imageClicked (image: Image) {
|
||||
onImageClicked (image: Image) {
|
||||
this.image = image
|
||||
this.dialog = 'new-game'
|
||||
},
|
||||
onImageEditClicked (image: Image) {
|
||||
this.image = image
|
||||
this.dialog = 'edit-image'
|
||||
},
|
||||
async uploadImage (data: any) {
|
||||
const formData = new FormData();
|
||||
formData.append('file', data.file, data.file.name);
|
||||
formData.append('title', data.title)
|
||||
formData.append('category', data.category)
|
||||
|
||||
const res = await fetch('/upload', {
|
||||
const res = await fetch('/api/upload', {
|
||||
method: 'post',
|
||||
body: formData,
|
||||
})
|
||||
return await res.json()
|
||||
},
|
||||
async saveImage (data: any) {
|
||||
const res = await fetch('/api/save-image', {
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
id: data.id,
|
||||
title: data.title,
|
||||
category: data.category,
|
||||
}),
|
||||
})
|
||||
return await res.json()
|
||||
},
|
||||
async onSaveImageClick(data: any) {
|
||||
await this.saveImage(data)
|
||||
this.dialog = ''
|
||||
await this.loadImages()
|
||||
},
|
||||
async postToGalleryClick(data: any) {
|
||||
await this.uploadImage(data)
|
||||
this.dialog = ''
|
||||
|
|
|
|||
|
|
@ -90,7 +90,36 @@ app.get('/api/index-data', (req, res) => {
|
|||
})
|
||||
})
|
||||
|
||||
app.post('/upload', (req, res) => {
|
||||
interface SaveImageRequestData {
|
||||
id: number
|
||||
title: string
|
||||
category: string
|
||||
}
|
||||
app.post('/api/save-image', bodyParser.json(), (req, res) => {
|
||||
const data = req.body as SaveImageRequestData
|
||||
db.update('images', {
|
||||
title: data.title,
|
||||
}, {
|
||||
id: data.id,
|
||||
})
|
||||
|
||||
db.delete('image_x_category', { image_id: data.id })
|
||||
|
||||
if (data.category) {
|
||||
const title = data.category
|
||||
const slug = Util.slug(title)
|
||||
const id = db.upsert('categories', { slug, title }, { slug }, 'id')
|
||||
if (id) {
|
||||
db.insert('image_x_category', {
|
||||
image_id: data.id,
|
||||
category_id: id,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
res.send({ ok: true })
|
||||
})
|
||||
app.post('/api/upload', (req, res) => {
|
||||
upload(req, res, async (err: any) => {
|
||||
if (err) {
|
||||
log.log(err)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue