fix(form): editing form for library objects

This commit is contained in:
upsiflu 2025-02-02 20:48:10 +01:00
parent 61e6b3fa0f
commit 32093949c7
3 changed files with 123 additions and 130 deletions

View File

@ -9,21 +9,19 @@ import { useStore } from '~/store'
import axios from 'axios'
import AttachmentInput from '~/components/common/AttachmentInput.vue'
import useEditConfigs from '~/composables/moderation/useEditConfigs'
import TagsSelector from '~/components/library/TagsSelector.vue'
import EditList from '~/components/library/EditList.vue'
import EditCard from '~/components/library/EditCard.vue'
import Layout from '~/components/ui/Layout.vue'
import Button from '~/components/ui/Button.vue'
import Link from '~/components/ui/Link.vue'
import Section from '~/components/ui/Section.vue'
import Spacer from '~/components/ui/Spacer.vue'
import Input from '~/components/ui/Input.vue'
import Textarea from "~/components/ui/Textarea.vue"
import Pills from "~/components/ui/Pills.vue"
import Alert from "~/components/ui/Alert.vue"
import AttachmentInput from '~/components/common/AttachmentInput.vue'
import useEditConfigs from '~/composables/moderation/useEditConfigs'
import EditList from '~/components/library/EditList.vue'
import EditCard from '~/components/library/EditCard.vue'
interface Props {
objectType: EditObjectType
object: EditObject
@ -147,14 +145,17 @@ const resetField = (fieldId: string) => {
:obj="submittedMutation"
:current-state="currentState"
/>
<Link
<Button
solid primary
@click.prevent="submittedMutation = null"
>
{{ t('components.library.EditForm.button.new') }}
</Link>
</Button>
</Alert>
<div v-else>
<Layout gap-32 v-else>
<!-- Previous edits -->
<edit-list
:filters="editListFilters"
:url="mutationsUrl"
@ -188,7 +189,10 @@ const resetField = (fieldId: string) => {
</empty-state>
</template>
</edit-list>
<Layout form
<!-- Add new edits -->
<form style="display: contents;"
@submit.prevent="submit()"
>
<div class="ui hidden divider" />
@ -213,86 +217,82 @@ const resetField = (fieldId: string) => {
>
{{ t('components.library.EditForm.message.noPermission') }}
</Alert>
<template v-if="values">
<div
v-for="fieldConfig in config.fields"
:key="fieldConfig.id"
class="ui field"
>
<template v-if="fieldConfig.type === 'text'">
<Spacer :size="64"/>
<Input
:id="fieldConfig.id"
v-model="values[fieldConfig.id]"
:type="fieldConfig.inputType || 'text'"
:required="fieldConfig.required"
:name="fieldConfig.id"
:label="fieldConfig.label"
/>
</template>
<template v-else-if="fieldConfig.type === 'license'">
<label :for="fieldConfig.id">{{ fieldConfig.label }}</label>
<Layout stack gap-8
v-if="values"
v-for="fieldConfig in config.fields"
:key="fieldConfig.id"
class="ui field"
>
<template v-if="fieldConfig.type === 'text'">
<Input
:id="fieldConfig.id"
v-model="values[fieldConfig.id]"
:type="fieldConfig.inputType || 'text'"
:required="fieldConfig.required"
:name="fieldConfig.id"
:label="fieldConfig.label"
/>
</template>
<template v-else-if="fieldConfig.type === 'license'">
<label :for="fieldConfig.id">{{ fieldConfig.label }}</label>
<select
:id="fieldConfig.id"
ref="license"
v-model="values[fieldConfig.id]"
:required="fieldConfig.required"
class="ui fluid search dropdown"
<select
:id="fieldConfig.id"
ref="license"
v-model="values[fieldConfig.id]"
:required="fieldConfig.required"
class="ui fluid search dropdown"
>
<option :value="null">
{{ t('components.library.EditForm.notApplicable') }}
</option>
<option
v-for="{ code, name } in licenses"
:key="code"
:value="code"
>
<option :value="null">
{{ t('components.library.EditForm.notApplicable') }}
</option>
<option
v-for="{ code, name } in licenses"
:key="code"
:value="code"
>
{{ name }}
</option>
</select>
{{ name }}
</option>
</select>
<Button
tiny
icon="bi-x"
form="noop"
@click.prevent="values[fieldConfig.id] = null"
>
{{ t('components.library.EditForm.button.clear') }}
</Button>
</template>
<template v-else-if="fieldConfig.type === 'content'">
<Textarea
:label="fieldConfig.label"
v-model="values[fieldConfig.id].text"
:field-id="fieldConfig.id"
:rows="3"
/>
</template>
<!-- TODO: Style Attachment Input -->
<template v-else-if="fieldConfig.type === 'attachment'">
<attachment-input
:id="fieldConfig.id"
v-model="values[fieldConfig.id]"
:initial-value="initialValues[fieldConfig.id]"
:required="fieldConfig.required"
:name="fieldConfig.id"
@delete="values[fieldConfig.id] = initialValues[fieldConfig.id]"
>
<span>{{ fieldConfig.label }}</span>
</attachment-input>
</template>
<template v-else-if="fieldConfig.type === 'tags'">
<Pills
:id="fieldConfig.id"
:label="fieldConfig.label"
ref="tags"
v-model="values[fieldConfig.id]"
required="fieldConfig.required"
>
<Button
tiny
icon="bi-x"
form="noop"
@click.prevent="values[fieldConfig.id] = null"
>
{{ t('components.library.EditForm.button.clear') }}
</Button>
</template>
<template v-else-if="fieldConfig.type === 'content'">
<label :for="fieldConfig.id">{{ fieldConfig.label }}</label>
<content-form
v-model="values[fieldConfig.id].text"
:field-id="fieldConfig.id"
:rows="3"
/>
</template>
<!-- TODO: Style Attachment Input -->
<template v-else-if="fieldConfig.type === 'attachment'">
<Spacer />
<attachment-input
:id="fieldConfig.id"
v-model="values[fieldConfig.id]"
:initial-value="initialValues[fieldConfig.id]"
:required="fieldConfig.required"
:name="fieldConfig.id"
@delete="values[fieldConfig.id] = initialValues[fieldConfig.id]"
>
<span>{{ fieldConfig.label }}</span>
</attachment-input>
</template>
<template v-else-if="fieldConfig.type === 'tags'">
<Spacer/>
<Pills
:id="fieldConfig.id"
:label="fieldConfig.label"
ref="tags"
v-model="values[fieldConfig.id]"
required="fieldConfig.required"
>
<Button
class="ui tiny basic left floated button"
icon="bi-x"
form="noop"
@click.prevent="values[fieldConfig.id] = []"
@ -300,40 +300,37 @@ const resetField = (fieldId: string) => {
{{ t('components.library.EditForm.button.clear') }}
</Button>
</Pills>
</template>
<div v-if="fieldValuesChanged(fieldConfig.id)">
<Button
tiny
alignSelf="end"
icon="bi-arrow-counterclockwise"
form="noop"
@click.prevent="resetField(fieldConfig.id)"
>
{{ t('components.library.EditForm.button.reset') }}
</Button>
</div>
</div>
</template>
</template>
<Button low-height
alignSelf="end"
icon="bi-arrow-counterclockwise"
form="noop"
:disabled="fieldValuesChanged(fieldConfig.id) ? undefined : true"
@click.prevent="resetField(fieldConfig.id)"
>
{{ t('components.library.EditForm.button.reset') }}
</Button>
</Layout>
<Spacer/>
<Textarea
id="change-summary"
v-model="summary"
name="change-summary"
rows="3"
initialLines="3"
:label="t('components.library.EditForm.label.summary')"
:placeholder="labels.summaryPlaceholder"
/>
<Button
v-if="objectType === 'track'"
:to="{name: 'library.tracks.detail', params: {id: object.id }}"
>
{{ t('components.library.EditForm.button.cancel') }}
</Button>
<Button
v-if="objectType === 'track'"
:to="{name: 'library.tracks.detail', params: {id: object.id }}"
>
{{ t('components.library.EditForm.button.cancel') }}
</Button>
</Textarea>
<Button
:class="['ui', 'right', 'floated', 'success', 'button']"
:isLoading="isLoading"
primary
type="submit"
:disabled="isLoading || !mutationPayload"
>
<span v-if="canEdit">
@ -343,6 +340,6 @@ const resetField = (fieldId: string) => {
{{ t('components.library.EditForm.button.suggest') }}
</span>
</Button>
</Layout>
</div>
</form>
</Layout>
</template>

View File

@ -20,6 +20,12 @@
> .pill-content {
padding: 0.45em 0.75em 0.55em 0.75em;
white-space: nowrap;
min-width: 28px;
border-radius: inherit;
&:focus-visible, &:focus {
outline: 2px solid var(--focus-ring-color);
outline-offset: 5px;
}
}
> .pill-image {

View File

@ -1,4 +1,4 @@
import type { Album, Artist, Content, Track, Actor } from '~/types'
import type { Album, Content, Track, Actor } from '~/types'
import { i18n } from '~/init/locale'
@ -16,8 +16,8 @@ export interface EditableConfigField extends ConfigField {
id: EditObjectType
}
export type EditObject = (Partial<Artist> | Partial<Album> | Partial<Track>) & { attributed_to: Actor }
export type EditObjectType = 'artist' | 'album' | 'track'
export type EditObject = (Partial<Album> | Partial<Track>) & { attributed_to: Actor }
export type EditObjectType = 'album' | 'track'
type Configs = Record<EditObjectType, { fields: (EditableConfigField|ConfigField)[] }>
const getContentValueRepr = (val: Content) => val.text
@ -48,25 +48,15 @@ export default (): Configs => {
type: 'tags',
required: true,
label: t('composables.moderation.useEditConfigs.tags.label'),
getValue: (obj) => { return obj.tags },
getValue: (obj) => ({
current: obj.tags || [],
others: [],
custom: [],
}),
getValueRepr: (tags: string[]) => tags.slice().sort().join('\n')
}
return {
artist: {
fields: [
{
id: 'name',
type: 'text',
required: true,
label: t('composables.moderation.useEditConfigs.artist.name'),
getValue: (artist) => (artist as Artist).name
},
description,
cover,
tags
]
},
album: {
fields: [
{