Add missing seasons endpoint and update metadata handling
Implemented a new endpoint to retrieve missing seasons for shows, enhancing the MetadataEndpoints class. Updated the IMetadataProvider interface to include a method for fetching season numbers. Adjusted the ShowSummaryDto to include other genres and modified the ListShowsQueryHandler to handle multiple genres. Enhanced the Omdb and Tmdb metadata providers to support season number retrieval. Updated frontend components to integrate missing seasons functionality, including API calls and UI elements for displaying missing seasons reports.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { Link } from '@tanstack/react-router'
|
||||
import { useMemo, useState } from 'react'
|
||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { qk } from '@/shared/api/query-keys'
|
||||
import {
|
||||
@@ -244,7 +244,9 @@ export function ShowsPanel() {
|
||||
<span className="text-muted-foreground">—</span>
|
||||
)}
|
||||
</td>
|
||||
<td className="px-4 py-2 text-muted-foreground">{show.primaryGenre ?? '—'}</td>
|
||||
<td className="px-4 py-2 text-muted-foreground">
|
||||
<GenreCell primary={show.primaryGenre} others={show.otherGenres} />
|
||||
</td>
|
||||
<td className="px-4 py-2 text-muted-foreground">{show.seasonCount}</td>
|
||||
<td className="px-4 py-2 text-muted-foreground">{show.episodeCount}</td>
|
||||
<td className="px-4 py-2">
|
||||
@@ -266,3 +268,70 @@ export function ShowsPanel() {
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/** Основной жанр шоу; остальные — под кнопкой «…», всплывающим списком. */
|
||||
function GenreCell({ primary, others }: Readonly<{ primary: string | null; others: string[] }>) {
|
||||
const { t } = useTranslation()
|
||||
const buttonRef = useRef<HTMLButtonElement>(null)
|
||||
// Всплывашку позиционируем fixed по кнопке: таблица лежит в контейнере с overflow-x-auto,
|
||||
// и absolute-список он обрезал бы по нижней границе. Прокрутка/ресайз сдвинули бы её мимо
|
||||
// кнопки — на них просто закрываем.
|
||||
const [at, setAt] = useState<{ left: number; top: number } | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (at == null) return
|
||||
const close = () => setAt(null)
|
||||
window.addEventListener('scroll', close, true)
|
||||
window.addEventListener('resize', close)
|
||||
return () => {
|
||||
window.removeEventListener('scroll', close, true)
|
||||
window.removeEventListener('resize', close)
|
||||
}
|
||||
}, [at])
|
||||
|
||||
if (!primary) return <span>—</span>
|
||||
if (others.length === 0) return <span>{primary}</span>
|
||||
|
||||
const toggle = () => {
|
||||
if (at) {
|
||||
setAt(null)
|
||||
return
|
||||
}
|
||||
const rect = buttonRef.current?.getBoundingClientRect()
|
||||
if (rect) setAt({ left: rect.left, top: rect.bottom + 4 })
|
||||
}
|
||||
|
||||
return (
|
||||
<span className="inline-flex items-center gap-1.5">
|
||||
{primary}
|
||||
<button
|
||||
ref={buttonRef}
|
||||
type="button"
|
||||
title={t('admin.shows.otherGenres')}
|
||||
aria-label={t('admin.shows.otherGenres')}
|
||||
aria-expanded={at != null}
|
||||
className="rounded-sm border border-border px-1 text-xs leading-tight hover:border-primary/60 hover:text-foreground"
|
||||
onClick={toggle}
|
||||
onBlur={() => setAt(null)}
|
||||
onKeyDown={(e) => e.key === 'Escape' && setAt(null)}
|
||||
>
|
||||
…
|
||||
</button>
|
||||
{at && (
|
||||
<div
|
||||
className="crt-panel fixed z-50 max-w-56 rounded-md border border-border bg-background px-2 py-1.5 text-xs"
|
||||
style={{ left: at.left, top: at.top }}
|
||||
>
|
||||
<p className="mb-1 text-[10px] uppercase tracking-wide text-muted-foreground">
|
||||
{t('admin.shows.otherGenres')}
|
||||
</p>
|
||||
<ul className="flex flex-col gap-0.5 text-foreground">
|
||||
{others.map((genre) => (
|
||||
<li key={genre}>{genre}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user