add ability to update image info

This commit is contained in:
Zutatensuppe 2021-05-22 14:26:04 +02:00
parent 6474c2fd8c
commit 92ed17efa5
12 changed files with 244 additions and 14 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -4,9 +4,9 @@
<meta charset="UTF-8">
<title>🧩 jigsaw.hyottoko.club</title>
<script type="module" crossorigin src="/assets/index.1c0291a2.js"></script>
<link rel="modulepreload" href="/assets/vendor.8616a479.js">
<link rel="stylesheet" href="/assets/index.c5b0553c.css">
<script type="module" crossorigin src="/assets/index.0aa9cc2a.js"></script>
<link rel="modulepreload" href="/assets/vendor.1ad14f11.js">
<link rel="stylesheet" href="/assets/index.dc049e4e.css">
</head>
<body>
<div id="app"></div>

View file

@ -1925,7 +1925,28 @@ app.get('/api/index-data', (req, res) => {
gamesFinished: games.filter(g => !!g.finished),
});
});
app.post('/upload', (req, res) => {
app.post('/api/save-image', bodyParser.json(), (req, res) => {
const data = req.body;
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) => {
if (err) {
log.log(err);

View 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>

View file

@ -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>

View file

@ -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>

View file

@ -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 = ''

View file

@ -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)

View file

@ -13,7 +13,7 @@ export default vite.defineConfig({
},
server: {
proxy: {
'^/((api|uploads)/.*|upload)': {
'^/(api|uploads)/.*': {
target: `http://${cfg.http.hostname}:${cfg.http.port}`,
secure: false,
},