Add rename show functionality: implement API endpoint for renaming shows, update Show model to support name changes, and enhance ShowMetadataCard UI for name input. Update translations for new UI elements to improve user experience.

This commit is contained in:
Leonid Pershin
2026-07-25 12:19:07 +03:00
parent 671bbb78bd
commit a34d929330
8 changed files with 86 additions and 21 deletions
@@ -16,6 +16,7 @@ import {
clearMetadata,
getMetadataProviders,
refreshEpisodesMetadata,
renameShow,
searchMetadata,
setShowOriginalName,
setShowPoster,
@@ -26,8 +27,8 @@ export function ShowMetadataCard({ show, onChanged }: { show: ShowDto; onChanged
const { t } = useTranslation()
const [galleryOpen, setGalleryOpen] = useState(false)
const [provider, setProvider] = useState('')
const [name, setName] = useState(show.name)
const [originalName, setOriginalName] = useState(show.originalName ?? '')
const [query, setQuery] = useState(show.originalName || show.name)
const [results, setResults] = useState<MetadataCandidate[]>([])
const [searched, setSearched] = useState(false)
const [description, setDescription] = useState(show.description ?? '')
@@ -52,9 +53,11 @@ export function ShowMetadataCard({ show, onChanged }: { show: ShowDto; onChanged
})
const effectiveProvider = provider || providers?.[0] || ''
// Метаданные ищем по оригинальному названию (иначе — по обычному).
const searchTerm = originalName.trim() || name.trim()
const search = useMutation({
mutationFn: () => searchMetadata(effectiveProvider, query.trim()),
mutationFn: () => searchMetadata(effectiveProvider, searchTerm),
onSuccess: (data) => {
setResults(data)
setSearched(true)
@@ -76,10 +79,11 @@ export function ShowMetadataCard({ show, onChanged }: { show: ShowDto; onChanged
const save = useMutation({
mutationFn: async () => {
const yearNum = year.trim() ? Number(year) : null
const nameChanged = originalName.trim() !== (show.originalName ?? '')
const infoChanged =
(description.trim() || null) !== (show.description ?? null) || yearNum !== (show.year ?? null)
if (nameChanged) await setShowOriginalName(show.id, originalName.trim() || null)
if (name.trim() && name.trim() !== show.name) await renameShow(show.id, name.trim())
if (originalName.trim() !== (show.originalName ?? ''))
await setShowOriginalName(show.id, originalName.trim() || null)
if (infoChanged)
await updateMetadata(show.id, { description: description.trim() || null, year: yearNum })
},
@@ -140,31 +144,29 @@ export function ShowMetadataCard({ show, onChanged }: { show: ShowDto; onChanged
/>
</div>
{/* Поиск + ручная правка */}
{/* Название + поиск + ручная правка */}
<div className="flex min-w-0 flex-1 flex-col gap-4">
<div className="flex flex-col gap-1.5">
<Label>{t('admin.metadata.name')}</Label>
<Input value={name} maxLength={256} onChange={(e) => setName(e.target.value)} />
</div>
<div className="flex flex-col gap-1.5">
<Label>{t('admin.metadata.originalName')}</Label>
<Input
value={originalName}
maxLength={256}
placeholder={t('admin.metadata.originalNamePlaceholder')}
onChange={(e) => setOriginalName(e.target.value)}
onChange={(e) => {
setOriginalName(e.target.value)
setSearched(false)
}}
/>
<p className="text-xs text-muted-foreground">{t('admin.metadata.originalNameHint')}</p>
</div>
{providers && providers.length > 0 && (
{providers && providers.length > 0 && (searched || results.length > 0) && (
<div className="flex flex-col gap-2">
<Label>{t('admin.metadata.searchLabel')}</Label>
<Input
value={query}
onChange={(e) => {
setQuery(e.target.value)
setSearched(false)
}}
placeholder={t('admin.metadata.searchPlaceholder')}
/>
{searched && results.length === 0 && (
<p className="text-xs text-muted-foreground">{t('admin.metadata.nothingFound')}</p>
)}
@@ -241,14 +243,14 @@ export function ShowMetadataCard({ show, onChanged }: { show: ShowDto; onChanged
<Button
size="sm"
variant="outline"
disabled={search.isPending || !query.trim()}
disabled={search.isPending || !searchTerm}
onClick={() => search.mutate()}
>
{t('admin.metadata.searchBtn')}
</Button>
</>
)}
<Button size="sm" disabled={save.isPending} onClick={() => save.mutate()}>
<Button size="sm" disabled={save.isPending || !name.trim()} onClick={() => save.mutate()}>
{t('common.save')}
</Button>
{linked && (
+4
View File
@@ -24,6 +24,10 @@ export function createShow(body: {
return apiRequest<CreatedIdResponse>('/admin/shows', { method: 'POST', body })
}
export function renameShow(id: string, name: string) {
return apiRequest<void>(`/admin/shows/${id}/name`, { method: 'PUT', body: { name } })
}
export function setShowOriginalName(id: string, originalName: string | null) {
return apiRequest<void>(`/admin/shows/${id}/original-name`, {
method: 'PUT',