From afe6cc24aa12c78e8a8b12d7ab7620d05f3e654f Mon Sep 17 00:00:00 2001 From: Leonid Pershin Date: Sat, 25 Jul 2026 12:03:25 +0300 Subject: [PATCH] Refactor ShowMetadataCard and API: remove obsolete poster upload functionality, streamline metadata saving process into a single mutation, and enhance UI for original name input. Update related state management and error handling for improved user experience. --- .../features/admin/shows/ShowMetadataCard.tsx | 81 +++++-------------- frontend/src/features/admin/shows/api.ts | 28 +------ 2 files changed, 22 insertions(+), 87 deletions(-) diff --git a/frontend/src/features/admin/shows/ShowMetadataCard.tsx b/frontend/src/features/admin/shows/ShowMetadataCard.tsx index 1736745..2d86c55 100644 --- a/frontend/src/features/admin/shows/ShowMetadataCard.tsx +++ b/frontend/src/features/admin/shows/ShowMetadataCard.tsx @@ -1,5 +1,5 @@ import { useMutation, useQuery } from '@tanstack/react-query' -import { useRef, useState } from 'react' +import { useState } from 'react' import { useTranslation } from 'react-i18next' import { HttpError } from '@/shared/api/client' import type { MetadataCandidate, ShowDto } from '@/shared/api/types' @@ -20,12 +20,10 @@ import { setShowOriginalName, setShowPoster, updateMetadata, - uploadPoster, } from './api' export function ShowMetadataCard({ show, onChanged }: { show: ShowDto; onChanged: () => void }) { const { t } = useTranslation() - const posterInput = useRef(null) const [galleryOpen, setGalleryOpen] = useState(false) const [provider, setProvider] = useState('') const [originalName, setOriginalName] = useState(show.originalName ?? '') @@ -63,15 +61,6 @@ export function ShowMetadataCard({ show, onChanged }: { show: ShowDto; onChanged }, onError, }) - const saveOriginal = useMutation({ - mutationFn: () => setShowOriginalName(show.id, originalName.trim() || null), - onSuccess: () => { - toast.success(t('settings.saved')) - setQuery(originalName.trim() || show.name) - onChanged() - }, - onError, - }) const apply = useMutation({ mutationFn: (externalId: string) => applyMetadata(show.id, effectiveProvider, externalId), onSuccess: () => { @@ -82,14 +71,21 @@ export function ShowMetadataCard({ show, onChanged }: { show: ShowDto; onChanged }, onError, }) - const saveManual = useMutation({ - mutationFn: () => - updateMetadata(show.id, { - description: description.trim() || null, - year: year.trim() ? Number(year) : null, - }), + // Одна кнопка «Сохранить»: пишем только изменённое. Оригинальное название идёт своим эндпоинтом + // (не трогает привязку к источнику), описание/год — ручной правкой метаданных. + 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 (infoChanged) + await updateMetadata(show.id, { description: description.trim() || null, year: yearNum }) + }, onSuccess: () => { toast.success(t('settings.saved')) + setQuery(originalName.trim() || show.name) changed() }, onError, @@ -103,11 +99,6 @@ export function ShowMetadataCard({ show, onChanged }: { show: ShowDto; onChanged }, onError, }) - const posterUpload = useMutation({ - mutationFn: (file: File) => uploadPoster(show.id, file), - onSuccess: changed, - onError, - }) const refreshEpisodes = useMutation({ mutationFn: () => refreshEpisodesMetadata(show.id), onSuccess: (count) => { @@ -139,25 +130,6 @@ export function ShowMetadataCard({ show, onChanged }: { show: ShowDto; onChanged {t('admin.metadata.noPoster')} )} - { - const file = e.target.files?.[0] - if (file) posterUpload.mutate(file) - e.target.value = '' - }} - /> - @@ -173,23 +145,12 @@ export function ShowMetadataCard({ show, onChanged }: { show: ShowDto; onChanged
-
- setOriginalName(e.target.value)} - /> - -
+ setOriginalName(e.target.value)} + />

{t('admin.metadata.originalNameHint')}

@@ -284,7 +245,7 @@ export function ShowMetadataCard({ show, onChanged }: { show: ShowDto; onChanged
- {linked && ( diff --git a/frontend/src/features/admin/shows/api.ts b/frontend/src/features/admin/shows/api.ts index ab3ed5c..4fc761e 100644 --- a/frontend/src/features/admin/shows/api.ts +++ b/frontend/src/features/admin/shows/api.ts @@ -1,4 +1,4 @@ -import { apiRequest, getAccessToken, HttpError } from '@/shared/api/client' +import { apiRequest } from '@/shared/api/client' import type { CreatedIdResponse, MetadataCandidate, @@ -84,29 +84,3 @@ export function setShowPoster(showId: string, imageId: string | null) { export function refreshEpisodesMetadata(showId: string) { return apiRequest(`/admin/metadata/shows/${showId}/refresh-episodes`, { method: 'POST' }) } - -/** Загрузка постера вручную (сырое тело, имя в query — как uploadMedia). */ -export function uploadPoster(showId: string, file: File): Promise { - return new Promise((resolve, reject) => { - const xhr = new XMLHttpRequest() - const q = new URLSearchParams({ fileName: file.name }) - xhr.open('PUT', `/api/admin/metadata/shows/${showId}/poster?${q.toString()}`) - const token = getAccessToken() - if (token) xhr.setRequestHeader('Authorization', `Bearer ${token}`) - xhr.onload = () => { - if (xhr.status >= 200 && xhr.status < 300) resolve() - else { - let detail = `HTTP ${xhr.status}` - try { - const p = JSON.parse(xhr.responseText) as { detail?: string; title?: string } - detail = p.detail ?? p.title ?? detail - } catch { - /* пусто */ - } - reject(new HttpError({ detail }, xhr.status)) - } - } - xhr.onerror = () => reject(new HttpError({ title: 'Network error' }, 0)) - xhr.send(file) - }) -}