Expanded edit fields. Added revision history to scene and user pages.
This commit is contained in:
9
pages/admin/+Page.vue
Normal file
9
pages/admin/+Page.vue
Normal file
@@ -0,0 +1,9 @@
|
||||
<template>
|
||||
<Admin>
|
||||
<h2 class="heading">Admin Panel</h2>
|
||||
</Admin>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import Admin from '#/components/admin/admin.vue';
|
||||
</script>
|
||||
13
pages/admin/+onBeforeRender.js
Normal file
13
pages/admin/+onBeforeRender.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import { render } from 'vike/abort'; /* eslint-disable-line import/extensions */
|
||||
|
||||
export function onBeforeRender(pageContext) {
|
||||
if (pageContext.user.role === 'user') {
|
||||
throw render(404);
|
||||
}
|
||||
|
||||
return {
|
||||
pageContext: {
|
||||
title: pageContext.routeParams.section,
|
||||
},
|
||||
};
|
||||
}
|
||||
18
pages/admin/revisions/+Page.vue
Normal file
18
pages/admin/revisions/+Page.vue
Normal file
@@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<Admin class="page">
|
||||
<Revisions
|
||||
:interactive="true"
|
||||
/>
|
||||
</Admin>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import Admin from '#/components/admin/admin.vue';
|
||||
import Revisions from '#/components/edit/revisions.vue';
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page {
|
||||
flex-grow: 1;
|
||||
}
|
||||
</style>
|
||||
30
pages/admin/revisions/+onBeforeRender.js
Normal file
30
pages/admin/revisions/+onBeforeRender.js
Normal file
@@ -0,0 +1,30 @@
|
||||
import { render } from 'vike/abort'; /* eslint-disable-line import/extensions */
|
||||
import { fetchSceneRevisions } from '#/src/scenes.js';
|
||||
|
||||
export async function onBeforeRender(pageContext) {
|
||||
if (!pageContext.user || pageContext.user.role === 'user') {
|
||||
throw render(404);
|
||||
}
|
||||
|
||||
const {
|
||||
revisions,
|
||||
actors,
|
||||
tags,
|
||||
movies,
|
||||
} = await fetchSceneRevisions(null, {
|
||||
isFinalized: false,
|
||||
limit: 50,
|
||||
}, pageContext.user);
|
||||
|
||||
return {
|
||||
pageContext: {
|
||||
title: pageContext.routeParams.section,
|
||||
pageProps: {
|
||||
revisions,
|
||||
actors,
|
||||
tags,
|
||||
movies,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
1
pages/admin/revisions/+route.js
Normal file
1
pages/admin/revisions/+route.js
Normal file
@@ -0,0 +1 @@
|
||||
export default '/admin/@section/*';
|
||||
@@ -286,7 +286,7 @@
|
||||
v-if="user"
|
||||
class="icon-link"
|
||||
target="_blank"
|
||||
:href="`/user/${user.username}/summaries?t=${selectedTemplate}`"
|
||||
:href="`/user/${user.username}/templates?t=${selectedTemplate}`"
|
||||
>
|
||||
<Icon
|
||||
v-tooltip="'Edit templates'"
|
||||
@@ -319,6 +319,23 @@
|
||||
>{{ userTemplate.name }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="user && user.role !== 'user'"
|
||||
class="scene-actions section"
|
||||
>
|
||||
<a
|
||||
:href="`/scene/edit/${scene.id}`"
|
||||
target="_blank"
|
||||
class="link"
|
||||
>Edit scene</a>
|
||||
|
||||
<a
|
||||
:href="`/scene/revisions/${scene.id}/${scene.slug}`"
|
||||
target="_blank"
|
||||
class="link"
|
||||
>Revisions</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -670,6 +687,13 @@ function copySummary() {
|
||||
}
|
||||
}
|
||||
|
||||
.scene-actions {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 2rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.icon-link {
|
||||
display: flex;
|
||||
height: auto;
|
||||
|
||||
@@ -1,6 +1,40 @@
|
||||
<template>
|
||||
<div class="editor">
|
||||
<form @submit.prevent>
|
||||
<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>
|
||||
|
||||
@@ -52,6 +86,15 @@
|
||||
@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"
|
||||
@@ -128,13 +171,24 @@
|
||||
</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"
|
||||
>Submit</button>
|
||||
>
|
||||
<template v-if="apply">Submit</template>
|
||||
<template v-else>Submit for review</template>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@@ -147,8 +201,13 @@ 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, patch } from '#/src/api.js';
|
||||
import {
|
||||
// get,
|
||||
post,
|
||||
} from '#/src/api.js';
|
||||
|
||||
const pageContext = inject('pageContext');
|
||||
|
||||
@@ -168,6 +227,11 @@ const fields = computed(() => [
|
||||
type: 'tags',
|
||||
value: scene.value.tags,
|
||||
},
|
||||
{
|
||||
key: 'movies',
|
||||
type: 'movies',
|
||||
value: scene.value.movies,
|
||||
},
|
||||
{
|
||||
key: 'title',
|
||||
type: 'string',
|
||||
@@ -211,6 +275,8 @@ const fields = computed(() => [
|
||||
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)) {
|
||||
@@ -219,6 +285,7 @@ function toggleField(item) {
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
editing.value.add(item.key);
|
||||
|
||||
if (Array.isArray(item.value)) {
|
||||
@@ -231,6 +298,8 @@ function toggleField(item) {
|
||||
|
||||
function setValue(item, event) {
|
||||
edits.value[item.key] = event.target.value;
|
||||
|
||||
console.log(edits.value);
|
||||
}
|
||||
|
||||
const timeUnits = ['h', 'm', 's'];
|
||||
@@ -241,7 +310,8 @@ function setDuration(unit, event) {
|
||||
|
||||
async function submit() {
|
||||
try {
|
||||
await patch(`/scenes/${scene.value.id}`, {
|
||||
await post('/revisions', {
|
||||
sceneId: scene.value.id,
|
||||
edits: {
|
||||
...edits.value,
|
||||
duration: edits.value.duration
|
||||
@@ -249,6 +319,7 @@ async function submit() {
|
||||
: undefined,
|
||||
},
|
||||
comment: comment.value,
|
||||
apply: apply.value,
|
||||
}, {
|
||||
successFeedback: 'Your revision has been submitted for approval.',
|
||||
appendErrorMessage: true,
|
||||
@@ -258,9 +329,9 @@ async function submit() {
|
||||
edits.value = {};
|
||||
comment.value = null;
|
||||
|
||||
scene.value = await get(`/scenes/${scene.value.id}`);
|
||||
submitted.value = true;
|
||||
|
||||
console.log(scene.value);
|
||||
// scene.value = await get(`/scenes/${scene.value.id}`);
|
||||
} catch (error) {
|
||||
// do nothing
|
||||
}
|
||||
@@ -270,6 +341,7 @@ async function submit() {
|
||||
<style scoped>
|
||||
.editor {
|
||||
flex-grow: 1;
|
||||
background: var(--background-dark-10);
|
||||
}
|
||||
|
||||
.editor-header {
|
||||
@@ -295,6 +367,10 @@ async function submit() {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.input {
|
||||
background: var(--background);
|
||||
}
|
||||
|
||||
.item-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -368,8 +444,10 @@ async function submit() {
|
||||
|
||||
.editor-actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 2rem;
|
||||
gap: 1.5rem;
|
||||
margin: .5rem 0;
|
||||
|
||||
.button {
|
||||
padding: .5rem 1rem;
|
||||
@@ -377,6 +455,15 @@ async function submit() {
|
||||
}
|
||||
}
|
||||
|
||||
.submitted {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
font-weight: bold;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
@media(--small) {
|
||||
.row {
|
||||
flex-direction: column;
|
||||
|
||||
@@ -1 +1 @@
|
||||
export default '/scene/@sceneId/*/edit';
|
||||
export default '/scene/edit/@sceneId/*';
|
||||
|
||||
37
pages/scene/revisions/+Page.vue
Normal file
37
pages/scene/revisions/+Page.vue
Normal file
@@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<div class="content">
|
||||
<div class="revs-header">
|
||||
<h2 class="heading">Revisions for "{{ scene.title }}"</h2>
|
||||
|
||||
<a
|
||||
:href="`/scene/${scene.id}/${scene.slug}`"
|
||||
target="_blank"
|
||||
class="link"
|
||||
>Go to scene</a>
|
||||
</div>
|
||||
|
||||
<Revisions />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { inject } from 'vue';
|
||||
|
||||
import Revisions from '#/components/edit/revisions.vue';
|
||||
|
||||
const pageContext = inject('pageContext');
|
||||
const scene = pageContext.pageProps.scene;
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.content {
|
||||
padding: 1rem;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.revs-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
34
pages/scene/revisions/+onBeforeRender.js
Normal file
34
pages/scene/revisions/+onBeforeRender.js
Normal file
@@ -0,0 +1,34 @@
|
||||
import { fetchScenesById, fetchSceneRevisions } from '#/src/scenes.js';
|
||||
|
||||
export async function onBeforeRender(pageContext) {
|
||||
const [scene] = await fetchScenesById([Number(pageContext.routeParams.sceneId)], {
|
||||
reqUser: pageContext.user,
|
||||
includeAssets: true,
|
||||
includePartOf: true,
|
||||
actorStashes: true,
|
||||
});
|
||||
|
||||
const {
|
||||
revisions,
|
||||
actors,
|
||||
tags,
|
||||
movies,
|
||||
} = await fetchSceneRevisions(null, {
|
||||
sceneId: scene.id,
|
||||
isFinalized: true,
|
||||
limit: 100,
|
||||
}, pageContext.user);
|
||||
|
||||
return {
|
||||
pageContext: {
|
||||
title: `Revisions for '${scene.title}'`,
|
||||
pageProps: {
|
||||
scene,
|
||||
revisions,
|
||||
actors,
|
||||
tags,
|
||||
movies,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
1
pages/scene/revisions/+route.js
Normal file
1
pages/scene/revisions/+route.js
Normal file
@@ -0,0 +1 @@
|
||||
export default '/scene/revisions/@sceneId/*';
|
||||
@@ -36,6 +36,12 @@
|
||||
class="domain nolink"
|
||||
:class="{ active: domain === 'templates' }"
|
||||
>Templates</a>
|
||||
|
||||
<a
|
||||
:href="`/user/${profile.username}/revisions`"
|
||||
class="domain nolink"
|
||||
:class="{ active: domain === 'revisions' }"
|
||||
>Revisions</a>
|
||||
</nav>
|
||||
|
||||
<Stashes v-if="domain === 'stashes'" />
|
||||
@@ -46,6 +52,14 @@
|
||||
:release="mockupRelease"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="domain === 'revisions' && profile.id === user?.id"
|
||||
class="profile-section revisions"
|
||||
>
|
||||
<h3 class="section-header heading">Revisions</h3>
|
||||
<Revisions />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -56,6 +70,7 @@ import { formatDistanceStrict } from 'date-fns';
|
||||
import Stashes from '#/components/stashes/stashes.vue';
|
||||
import Alerts from '#/components/alerts/alerts.vue';
|
||||
import Summaries from '#/components/scenes/summaries.vue';
|
||||
import Revisions from '#/components/edit/revisions.vue';
|
||||
|
||||
const pageContext = inject('pageContext');
|
||||
const domain = pageContext.routeParams.domain;
|
||||
@@ -125,8 +140,9 @@ const mockupRelease = {
|
||||
<style scoped>
|
||||
.page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
flex-grow: 1;
|
||||
justify-content: center;
|
||||
background: var(--background-base-10);
|
||||
}
|
||||
|
||||
@@ -200,6 +216,12 @@ const mockupRelease = {
|
||||
}
|
||||
}
|
||||
|
||||
.revisions {
|
||||
width: 100%; /* necessary for FF */
|
||||
box-sizing: border-box;
|
||||
padding: 0 1rem;
|
||||
}
|
||||
|
||||
@media(--compact) {
|
||||
.domains {
|
||||
padding: .5rem 1rem;
|
||||
|
||||
@@ -3,19 +3,33 @@ import { render } from 'vike/abort'; /* eslint-disable-line import/extensions */
|
||||
import { fetchUser } from '#/src/users.js';
|
||||
import { fetchUserStashes } from '#/src/stashes.js';
|
||||
import { fetchAlerts } from '#/src/alerts.js';
|
||||
import { fetchSceneRevisions } from '#/src/scenes.js';
|
||||
|
||||
export async function onBeforeRender(pageContext) {
|
||||
const [profile, alerts] = await Promise.all([
|
||||
const [profile, alerts, userRevisions] = await Promise.all([
|
||||
fetchUser(pageContext.routeParams.username, {}, pageContext.user),
|
||||
pageContext.routeParams.username === pageContext.user?.username
|
||||
pageContext.routeParams.domain === 'stashes' && pageContext.routeParams.username === pageContext.user?.username
|
||||
? fetchAlerts(pageContext.user)
|
||||
: [],
|
||||
pageContext.routeParams.domain === 'revisions' && pageContext.routeParams.username === pageContext.user?.username
|
||||
? fetchSceneRevisions(null, {
|
||||
userId: pageContext.user.id,
|
||||
limit: 100,
|
||||
}, pageContext.user)
|
||||
: {},
|
||||
]);
|
||||
|
||||
if (!profile) {
|
||||
throw render(404, `Cannot find user '${pageContext.routeParams.username}'.`);
|
||||
}
|
||||
|
||||
const {
|
||||
revisions,
|
||||
actors,
|
||||
tags,
|
||||
movies,
|
||||
} = userRevisions;
|
||||
|
||||
const stashes = await fetchUserStashes(profile.id, pageContext.user);
|
||||
|
||||
return {
|
||||
@@ -25,6 +39,10 @@ export async function onBeforeRender(pageContext) {
|
||||
profile, // differentiate from authed 'user'
|
||||
stashes,
|
||||
alerts,
|
||||
revisions,
|
||||
actors,
|
||||
tags,
|
||||
movies,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user