Implement bulk tagging and enriching of shows with new API endpoints and frontend integration
Added new API endpoints for bulk tagging and enriching shows, allowing for mass updates of genres and audience ratings. Implemented backend logic to handle bulk operations and updated the Dependency Injection configuration to include necessary services. Enhanced the frontend with new components for selecting shows and applying bulk actions, improving the user experience for managing multiple shows simultaneously. Localization updates were made to support these new features in both English and Russian.
This commit is contained in:
@@ -19,6 +19,7 @@ import { sortRows, useTableSort } from '@/shared/lib/table-sort'
|
||||
import { SortHeader } from '@/shared/ui/sortable'
|
||||
import { listGenres } from '@/features/admin/genres/api'
|
||||
import { createShow, deleteShow, listShows } from './api'
|
||||
import { BulkTagBar } from './BulkTagBar'
|
||||
|
||||
const PAGE_SIZE = 20
|
||||
|
||||
@@ -40,6 +41,8 @@ export function ShowsPanel() {
|
||||
|
||||
// Фильтр по жанру — серверный: в списке видно только основной жанр, а отбирать нужно и по остальным.
|
||||
const [genreFilter, setGenreFilter] = useState('all')
|
||||
// Отмеченные для массовых действий. Живут отдельно от фильтров: сузил список — выбор не потерялся.
|
||||
const [selected, setSelected] = useState<string[]>([])
|
||||
const { data: genres } = useQuery({ queryKey: qk.genres.all, queryFn: listGenres })
|
||||
const { data, isLoading } = useQuery({
|
||||
queryKey: qk.shows.byGenre(genreFilter),
|
||||
@@ -171,10 +174,26 @@ export function ShowsPanel() {
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{selected.length > 0 && <BulkTagBar showIds={selected} onDone={() => setSelected([])} />}
|
||||
|
||||
<div className="crt-panel overflow-x-auto rounded-md">
|
||||
<table className="w-full text-sm">
|
||||
<thead className="border-b border-border text-left text-muted-foreground">
|
||||
<tr>
|
||||
<th className="w-8 px-4 py-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
aria-label={t('admin.shows.bulk.selectPage')}
|
||||
checked={pageItems.length > 0 && pageItems.every((s) => selected.includes(s.id))}
|
||||
onChange={(e) =>
|
||||
setSelected((current) =>
|
||||
e.target.checked
|
||||
? [...new Set([...current, ...pageItems.map((s) => s.id)])]
|
||||
: current.filter((id) => !pageItems.some((s) => s.id === id)),
|
||||
)
|
||||
}
|
||||
/>
|
||||
</th>
|
||||
<SortHeader
|
||||
label={t('admin.shows.name')}
|
||||
sortKey="name"
|
||||
@@ -217,13 +236,27 @@ export function ShowsPanel() {
|
||||
<tbody>
|
||||
{isLoading && (
|
||||
<tr>
|
||||
<td className="px-4 py-3 text-muted-foreground" colSpan={7}>
|
||||
<td className="px-4 py-3 text-muted-foreground" colSpan={8}>
|
||||
{t('common.loading')}
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
{pageItems.map((show) => (
|
||||
<tr key={show.id} className="border-b border-border last:border-0">
|
||||
<td className="px-4 py-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
aria-label={show.name}
|
||||
checked={selected.includes(show.id)}
|
||||
onChange={() =>
|
||||
setSelected((current) =>
|
||||
current.includes(show.id)
|
||||
? current.filter((id) => id !== show.id)
|
||||
: [...current, show.id],
|
||||
)
|
||||
}
|
||||
/>
|
||||
</td>
|
||||
<td className="px-4 py-2">
|
||||
<Link
|
||||
to="/admin/shows/$showId"
|
||||
|
||||
Reference in New Issue
Block a user