<template> <div class="editor"> <p v-if="submitted" class="submitted" > <template v-if="apply">Your revision has been submitted. Thank you for your contribution!</template> <template v-else>Your revision has been submitted for review. Thank you for your contribution!</template> <ul> <li> <a :href="`/scene/${scene.id}/${scene.slug}`" class="link" >Return to scene</a> </li> <li> <a :href="`/scene/edit/${scene.id}`" class="link" >Make another edit</a> </li> <li> <a :href="`/user/${user.username}`" class="link" >Go to profile</a> </li> </ul> </p> <form v-else @submit.prevent > <div class="editor-header"> <h2 class="heading ellipsis">Edit scene #{{ scene.id }}</h2> <a :href="`/scene/${scene.id}/${scene.slug}`" target="_blank" class="link noshrink" >Go to scene</a> </div> <ul class="nolist"> <li v-for="item in fields" :key="`item-${item.key}`" class="row" > <div class="item-header"> <div class="key">{{ item.label || item.key }}</div> <div class="item-actions"> <Icon v-if="!item.forced" icon="pencil5" :class="{ active: editing.has(item.key) }" @click="toggleField(item)" /> </div> </div> <div class="value" :class="{ disabled: !editing.has(item.key) }" > <EditActors v-if="item.type === 'actors'" :scene="scene" :item="item" :edits="edits" :editing="editing" @actors="(actors) => { edits.actors = actors; }" /> <EditTags v-if="item.type === 'tags'" :scene="scene" :item="item" :edits="edits" :editing="editing" @tags="(tags) => { edits.tags = tags; }" /> <EditMovies v-if="item.type === 'movies'" :scene="scene" :item="item" :edits="edits" :editing="editing" @movies="(movies) => { edits.movies = movies; }" /> <input v-if="item.type === 'string'" :value="edits[item.key] || item.value" class="string input" :disabled="!editing.has(item.key)" @input="setValue(item, $event)" > <textarea v-if="item.type === 'text'" :value="edits[item.key] || item.value" :placeholder="item.placeholder" rows="3" class="text input" :disabled="!editing.has(item.key)" @input="setValue(item, $event)" /> <input v-if="item.type === 'date'" type="datetime-local" :value="edits[item.key] || item.value" class="date input" :disabled="!editing.has(item.key)" @input="setValue(item, $event)" > <div v-if="item.type === 'duration'" class="duration" > <input type="number" class="input" :value="item.value[0]" min="0" max="100" :disabled="!editing.has(item.key)" @input="setDuration('h', $event)" >H <input type="number" class="input" :value="item.value[1]" min="0" max="59" :disabled="!editing.has(item.key)" @input="setDuration('m', $event)" >M <input type="number" class="input" :value="item.value[2]" min="0" max="59" :disabled="!editing.has(item.key)" @input="setDuration('s', $event)" >S </div> </div> </li> </ul> <div class="editor-footer"> <div class="comment"> <textarea v-model="comment" rows="3" placeholder="Please provide verifiable information supporting your edits." class="text input" /> </div> <div class="editor-actions"> <Checkbox v-if="user.role !== 'user'" label="Approve and apply immediately" :checked="apply" :disabled="editing.size === 0" @change="(checked) => apply = checked" /> <!-- we don't want the return key to submit the form --> <button class="button button-primary" type="button" :disabled="editing.size === 0" @click="submit" > <template v-if="apply">Submit</template> <template v-else>Submit for review</template> </button> </div> </div> </form> </div> </template> <script setup> import { ref, computed, inject } from 'vue'; import { format } from 'date-fns'; import EditActors from '#/components/edit/actors.vue'; import EditTags from '#/components/edit/tags.vue'; import EditMovies from '#/components/edit/movies.vue'; import Checkbox from '#/components/form/checkbox.vue'; import { // get, post, } from '#/src/api.js'; const pageContext = inject('pageContext'); const user = pageContext.user; const scene = ref(pageContext.pageProps.scene); // console.log(scene); const fields = computed(() => [ { key: 'actors', type: 'actors', value: scene.value.actors, }, { key: 'tags', type: 'tags', value: scene.value.tags, }, { key: 'movies', type: 'movies', value: scene.value.movies, }, { key: 'title', type: 'string', value: scene.value.title, }, { key: 'description', type: 'text', value: scene.value.description, }, { key: 'date', type: 'date', value: scene.value.date ? format(scene.value.date, 'yyyy-MM-dd hh:mm') : null, }, { key: 'duration', type: 'duration', value: [Math.floor(scene.value.duration / 3600), Math.floor((scene.value.duration % 3600) / 60), scene.value.duration % 60], }, { key: 'productionDate', label: 'production date', type: 'date', value: scene.value.productionDate ? format(scene.value.productionDate, 'yyyy-MM-dd hh:mm') : null, }, ...(user.role === 'user' ? [] : [{ key: 'comment', type: 'text', placeholder: 'Do NOT use this field to summarize and clarify your revision. This field is for permanent notes and comments regarding the scene or database entry itself.', value: scene.value.comment, }]), ]); const editing = ref(new Set()); const edits = ref({}); const comment = ref(null); const apply = ref(user.role !== 'user'); const submitted = ref(false); function toggleField(item) { if (editing.value.has(item.key)) { editing.value.delete(item.key); delete edits.value[item.key]; return; } editing.value.add(item.key); if (Array.isArray(item.value)) { edits.value[item.key] = item.value.map((value) => value.hash || value.id); return; } edits.value[item.key] = item.value; } function setValue(item, event) { edits.value[item.key] = event.target.value; console.log(edits.value); } const timeUnits = ['h', 'm', 's']; function setDuration(unit, event) { edits.value.duration[timeUnits.indexOf(unit)] = Number(event.target.value); } async function submit() { try { await post('/revisions', { sceneId: scene.value.id, edits: { ...edits.value, duration: edits.value.duration ? (((edits.value.duration[0] || 0) * 3600) + ((edits.value.duration[1] || 0) * 60) + (edits.value.duration[2] || 0)) || null : undefined, }, comment: comment.value, apply: apply.value, }, { successFeedback: 'Your revision has been submitted for approval.', appendErrorMessage: true, }); editing.value = new Set(); edits.value = {}; comment.value = null; submitted.value = true; // scene.value = await get(`/scenes/${scene.value.id}`); } catch (error) { // do nothing } } </script> <style scoped> .editor { flex-grow: 1; background: var(--background-dark-10); } .editor-header { display: flex; justify-content: space-between; align-items: center; padding: 1rem; } .heading { margin: 0; } .row { display: flex; align-items: center; padding: .25rem 1rem; } .key { width: 8rem; text-transform: capitalize; font-weight: bold; } .input { background: var(--background); } .item-header { display: flex; align-items: center; } .value { flex-grow: 1; .input { width: 100%; &:disabled { color: var(--glass-strong-10); background: none; border: solid 1px var(--glass-weak-30); } } .duration { .input { width: 5rem; margin-right: .25rem; &:not(:first-child) { margin-left: .75rem; } } } &.disabled { pointer-events: none; } } .item-actions { .icon { padding: .25rem 1rem; fill: var(--glass); overflow: hidden; &:hover { cursor: pointer; fill: var(--text); } &.active { fill: var(--primary); } } } .editor-footer { display: flex; flex-direction: column; align-items: center; gap: 1rem; padding: 1rem 1rem 0 1rem; border-top: solid 1px var(--primary-light-30); margin: 1rem 0; } .comment { width: 100%; flex-shrink: 0; .input { width: 100%; resize: vertical; } } .editor-actions { display: flex; flex-direction: column; align-items: center; gap: 1.5rem; margin: .5rem 0; .button { padding: .5rem 1rem; font-size: 1.1rem; } } .submitted { display: flex; flex-direction: column; align-items: center; padding: 1rem; font-weight: bold; line-height: 1.5; } @media(--small) { .row { flex-direction: column; align-items: stretch; margin-bottom: .25rem; } .item-header { margin-bottom: .25rem; } .key { flex-grow: 1; } } </style>