Added new actor dialog to admin panel, removed create mode from actor edit page.
This commit is contained in:
@@ -25,7 +25,8 @@
|
|||||||
.button {
|
.button {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
align-items: stretch;
|
/* align-items: stretch; */
|
||||||
|
align-items: center;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: .5rem;
|
padding: .5rem;
|
||||||
border: none;
|
border: none;
|
||||||
|
|||||||
175
components/actors/add.vue
Normal file
175
components/actors/add.vue
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
<template>
|
||||||
|
<Dialog
|
||||||
|
title="Add actor"
|
||||||
|
@close="emit('close')"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-if="isSubmitted && newActor"
|
||||||
|
class="dialog-body"
|
||||||
|
>
|
||||||
|
<strong>Added '{{ newActor.name }}' (#{{ newActor.id }})</strong>
|
||||||
|
|
||||||
|
<ul
|
||||||
|
v-if="isSubmitted && newActor"
|
||||||
|
class="dialog-body options"
|
||||||
|
>
|
||||||
|
<li class="option">
|
||||||
|
<a
|
||||||
|
:href="`/actor/edit/${newActor.id}/${newActor.slug}`"
|
||||||
|
target="_blank"
|
||||||
|
class="link"
|
||||||
|
>Edit bio</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="option">
|
||||||
|
<a
|
||||||
|
:href="`/actor/${newActor.id}/${newActor.slug}`"
|
||||||
|
target="_blank"
|
||||||
|
class="link"
|
||||||
|
>Go to actor</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="option">
|
||||||
|
<span
|
||||||
|
class="link"
|
||||||
|
@click="newActor = null; isSubmitted = false;"
|
||||||
|
>Add another actor</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form
|
||||||
|
v-else
|
||||||
|
class="dialog-body"
|
||||||
|
@submit.prevent="addActor"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
ref="nameInput"
|
||||||
|
v-model="name"
|
||||||
|
class="input"
|
||||||
|
placeholder="Name"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
|
||||||
|
<select
|
||||||
|
v-model="gender"
|
||||||
|
placeholder="Gender"
|
||||||
|
class="input"
|
||||||
|
>
|
||||||
|
<option value="female">Female</option>
|
||||||
|
<option value="male">Male</option>
|
||||||
|
<option value="transsexual">Transsexual</option>
|
||||||
|
<option value="other">Other</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<Checkbox
|
||||||
|
label="Allow global match"
|
||||||
|
:checked="allowGlobalMatch"
|
||||||
|
@change="(isChecked) => allowGlobalMatch = isChecked"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="dialog-actions">
|
||||||
|
<Ellipsis v-if="isSubmitting" />
|
||||||
|
|
||||||
|
<button
|
||||||
|
v-else
|
||||||
|
class="button button-primary"
|
||||||
|
>Add actor</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</Dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, onMounted } from 'vue';
|
||||||
|
|
||||||
|
import Dialog from '#/components/dialog/dialog.vue';
|
||||||
|
import Ellipsis from '#/components/loading/ellipsis.vue';
|
||||||
|
import Checkbox from '#/components/form/checkbox.vue';
|
||||||
|
|
||||||
|
import { post } from '#/src/api.js';
|
||||||
|
import events from '#/src/events.js';
|
||||||
|
|
||||||
|
const emit = defineEmits(['close']);
|
||||||
|
|
||||||
|
const nameInput = ref(null);
|
||||||
|
const isSubmitting = ref(false);
|
||||||
|
const isSubmitted = ref(false);
|
||||||
|
|
||||||
|
const name = ref(null);
|
||||||
|
const gender = ref('female');
|
||||||
|
const allowGlobalMatch = ref(true);
|
||||||
|
|
||||||
|
const newActor = ref(null);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
nameInput.value.focus();
|
||||||
|
});
|
||||||
|
|
||||||
|
async function addActor() {
|
||||||
|
isSubmitting.value = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { actor } = await post('/actors', {
|
||||||
|
actor: {
|
||||||
|
name: name.value,
|
||||||
|
gender: gender.value,
|
||||||
|
allowGlobalMatch: allowGlobalMatch.value,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
successFeedback: 'Actor has been added.',
|
||||||
|
appendErrorMessage: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
name.value = null;
|
||||||
|
gender.value = null;
|
||||||
|
allowGlobalMatch.value = true;
|
||||||
|
|
||||||
|
newActor.value = actor;
|
||||||
|
} catch (error) {
|
||||||
|
events.emit('feedback', {
|
||||||
|
type: 'error',
|
||||||
|
message: error.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
isSubmitting.value = false;
|
||||||
|
isSubmitted.value = true;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.dialog-body {
|
||||||
|
width: 20rem;
|
||||||
|
max-width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 1rem;
|
||||||
|
gap: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.load-container {
|
||||||
|
width: 100%;
|
||||||
|
display: inline-flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0;
|
||||||
|
height: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.options {
|
||||||
|
padding: 0 1.5rem;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-actions {
|
||||||
|
margin-top: .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-primary {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -373,8 +373,6 @@ import {
|
|||||||
post,
|
post,
|
||||||
} from '#/src/api.js';
|
} from '#/src/api.js';
|
||||||
|
|
||||||
import events from '#/src/events.js';
|
|
||||||
|
|
||||||
const pageContext = inject('pageContext');
|
const pageContext = inject('pageContext');
|
||||||
|
|
||||||
const user = pageContext.user;
|
const user = pageContext.user;
|
||||||
@@ -642,48 +640,7 @@ const groupMap = {
|
|||||||
weight: 'size',
|
weight: 'size',
|
||||||
};
|
};
|
||||||
|
|
||||||
async function submitCreate() {
|
|
||||||
submitting.value = true;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const newActor = {
|
|
||||||
actor: Object.fromEntries(Array.from(['name', 'gender']).flatMap((key) => {
|
|
||||||
if (!edits.value[key]) {
|
|
||||||
throw new Error(`Missing ${key}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (edits.value[key] && typeof edits.value[key] === 'object' && !Array.isArray(edits.value[key])) {
|
|
||||||
return Object.entries(edits.value[key]).map(([valueKey, value]) => [keyMap[key]?.[valueKey] || valueKey, value]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return [[key, edits.value[key]]];
|
|
||||||
})),
|
|
||||||
comment: comment.value,
|
|
||||||
};
|
|
||||||
|
|
||||||
const { actor: createdActor } = await post('/actors', newActor, {
|
|
||||||
successFeedback: 'Actor has been added.',
|
|
||||||
appendErrorMessage: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
actor.value = createdActor;
|
|
||||||
} catch (error) {
|
|
||||||
events.emit('feedback', {
|
|
||||||
type: 'error',
|
|
||||||
message: error.message,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
submitting.value = false;
|
|
||||||
submitted.value = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function submit() {
|
async function submit() {
|
||||||
if (!actor.value) {
|
|
||||||
submitCreate();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
submitting.value = true;
|
submitting.value = true;
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// import { redirect } from 'vike/abort'; /* eslint-disable-line import/extensions */
|
// import { redirect } from 'vike/abort'; /* eslint-disable-line import/extensions */
|
||||||
import { match } from 'path-to-regexp';
|
import { match } from 'path-to-regexp';
|
||||||
|
|
||||||
const path = '/actor/(edit|new)/:actorId?/:actorSlug?';
|
const path = '/actor/edit/:actorId/:actorSlug?';
|
||||||
const urlMatch = match(path, { decode: decodeURIComponent });
|
const urlMatch = match(path, { decode: decodeURIComponent });
|
||||||
|
|
||||||
export default (pageContext) => {
|
export default (pageContext) => {
|
||||||
|
|||||||
@@ -21,6 +21,11 @@
|
|||||||
:disabled="selectedActors.size === 0"
|
:disabled="selectedActors.size === 0"
|
||||||
@click="showMergeDialog = true"
|
@click="showMergeDialog = true"
|
||||||
><Icon icon="make-group" />Merge</button>
|
><Icon icon="make-group" />Merge</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="button"
|
||||||
|
@click="showAddDialog = true"
|
||||||
|
><Icon icon="plus3" />Add actor</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -84,6 +89,17 @@
|
|||||||
@change="(isChecked) => selectActors(actor, isChecked, index)"
|
@change="(isChecked) => selectActors(actor, isChecked, index)"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<a
|
||||||
|
:href="`/actor/edit/${actor.id}/${actor.slug}`"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
v-tooltip="'Edit bio'"
|
||||||
|
icon="pencil5"
|
||||||
|
class="actor-action action-merge"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
|
||||||
<Icon
|
<Icon
|
||||||
v-tooltip="'Merge'"
|
v-tooltip="'Merge'"
|
||||||
icon="make-group"
|
icon="make-group"
|
||||||
@@ -104,11 +120,16 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<Merge
|
<MergeActors
|
||||||
v-if="showMergeDialog"
|
v-if="showMergeDialog"
|
||||||
:actors="activeActor ? [activeActor] : actors.filter((actor) => selectedActors.has(actor.id))"
|
:actors="activeActor ? [activeActor] : actors.filter((actor) => selectedActors.has(actor.id))"
|
||||||
@close="showMergeDialog = false; activeActor = null;"
|
@close="showMergeDialog = false; activeActor = null;"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<AddActor
|
||||||
|
v-if="showAddDialog"
|
||||||
|
@close="showAddDialog = false"
|
||||||
|
/>
|
||||||
</Admin>
|
</Admin>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -117,7 +138,8 @@ import { ref, inject, onMounted } from 'vue';
|
|||||||
|
|
||||||
import Admin from '#/components/admin/admin.vue';
|
import Admin from '#/components/admin/admin.vue';
|
||||||
import Checkbox from '#/components/form/checkbox.vue';
|
import Checkbox from '#/components/form/checkbox.vue';
|
||||||
import Merge from '#/components/actors/merge.vue';
|
import MergeActors from '#/components/actors/merge.vue';
|
||||||
|
import AddActor from '#/components/actors/add.vue';
|
||||||
|
|
||||||
import getPath from '#/src/get-path.js';
|
import getPath from '#/src/get-path.js';
|
||||||
import navigate from '#/src/navigate.js';
|
import navigate from '#/src/navigate.js';
|
||||||
@@ -133,6 +155,7 @@ const actorQuery = ref(urlParsed.search.q || null);
|
|||||||
const lastSelectedIndex = ref(null);
|
const lastSelectedIndex = ref(null);
|
||||||
const holdingShift = ref(false);
|
const holdingShift = ref(false);
|
||||||
const showMergeDialog = ref(false);
|
const showMergeDialog = ref(false);
|
||||||
|
const showAddDialog = ref(false);
|
||||||
|
|
||||||
function selectActors(selectedActor, isChecked, index) {
|
function selectActors(selectedActor, isChecked, index) {
|
||||||
const [start, end] = holdingShift.value
|
const [start, end] = holdingShift.value
|
||||||
@@ -155,7 +178,7 @@ function selectActors(selectedActor, isChecked, index) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function searchActors() {
|
async function searchActors() {
|
||||||
navigate('/admin/actors', { q: actorQuery.value }, { redirect: true });
|
navigate('/admin/actors', { q: actorQuery.value || undefined }, { redirect: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
@@ -192,7 +215,9 @@ onMounted(() => {
|
|||||||
.header-actions {
|
.header-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
gap: .5rem;
|
gap: .5rem;
|
||||||
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.actors-header tr {
|
.actors-header tr {
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export default async function onBeforeRender(pageContext) {
|
|||||||
query: pageContext.urlParsed.search.q,
|
query: pageContext.urlParsed.search.q,
|
||||||
}, {
|
}, {
|
||||||
limit: 100,
|
limit: 100,
|
||||||
// order: pageContext.urlParsed.search.order?.split('.') || ['likes', 'desc'],
|
order: pageContext.urlParsed.search.order?.split('.') || ['likes', 'desc'],
|
||||||
}, pageContext.user);
|
}, pageContext.user);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -548,6 +548,7 @@ function curateActorEntry(actor, context) {
|
|||||||
slug: slugify(actor.name),
|
slug: slugify(actor.name),
|
||||||
entry_id: nanoid(), // allows for manual creation of multiple actors with the same name
|
entry_id: nanoid(), // allows for manual creation of multiple actors with the same name
|
||||||
gender: actor.gender,
|
gender: actor.gender,
|
||||||
|
allow_global_match: actor.allowGlobalMatch,
|
||||||
comment: context?.comment,
|
comment: context?.comment,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user