Update CollectionSuggestions and GroupSuggestions to default to collapsed state; enhance GenresPanel with pagination and sorting improvements
ci / build-backend (push) Failing after 33s
ci / build-frontend (push) Successful in 1m3s
ci / tests (push) Skipped
ci / sonar (push) Skipped

Modified CollectionSuggestions and GroupSuggestions components to start in a collapsed state for better UI experience. Enhanced GenresPanel by implementing pagination and updating sorting functionality to ensure a more manageable display of genre data, improving overall usability.
This commit is contained in:
Leonid Pershin
2026-07-27 12:16:42 +03:00
parent ef3328ec6f
commit 986cf02b0d
3 changed files with 33 additions and 7 deletions
@@ -14,6 +14,7 @@ import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from '
import { Input } from '@/shared/ui/input'
import { Label } from '@/shared/ui/label'
import { sortRows, useTableSort } from '@/shared/lib/table-sort'
import { Pager } from '@/shared/ui/pager'
import { SortHeader } from '@/shared/ui/sortable'
import { createGenre, deleteGenre, listGenres, updateGenre } from './api'
@@ -36,6 +37,13 @@ const editSchema = z.object({
type CreateValues = z.infer<typeof createSchema>
type EditValues = z.infer<typeof editSchema>
/**
* Жанров бывает под сотню (базовый набор плюс заведённые руками), и все они грузятся одним
* запросом — список нужен целиком селектам жанра по всей админке. Поэтому страницы режутся здесь,
* а не на сервере: таблица остаётся читаемой, а пикеры продолжают получать полный справочник.
*/
const PAGE_SIZE = 20
/** Псевдонимы вводятся одной строкой через запятую — их редко больше десятка. */
function parseAliases(value: string | undefined): string[] {
return (value ?? '')
@@ -52,6 +60,8 @@ export function GenresPanel() {
queryFn: listGenres,
})
const { sort, toggle } = useTableSort('sortOrder', false)
const [page, setPage] = useState(1)
const sortedGenres = sortRows(genres ?? [], sort, {
sortOrder: (g) => g.sortOrder,
name: (g) => g.name.toLowerCase(),
@@ -59,6 +69,16 @@ export function GenresPanel() {
showCount: (g) => g.showCount,
})
// Страница может уехать за конец списка после удаления — тогда показываем последнюю.
const totalPages = Math.max(1, Math.ceil(sortedGenres.length / PAGE_SIZE))
const current = Math.min(page, totalPages)
const visible = sortedGenres.slice((current - 1) * PAGE_SIZE, current * PAGE_SIZE)
const sortColumn = (key: string) => {
setPage(1)
toggle(key)
}
const invalidate = () => queryClient.invalidateQueries({ queryKey: qk.genres.all })
const reportError = useApiError()
@@ -145,26 +165,26 @@ export function GenresPanel() {
label={t('admin.genres.order')}
sortKey="sortOrder"
sort={sort}
onToggle={toggle}
onToggle={sortColumn}
/>
<SortHeader
label={t('admin.genres.name')}
sortKey="name"
sort={sort}
onToggle={toggle}
onToggle={sortColumn}
/>
<SortHeader
label={t('admin.genres.slug')}
sortKey="slug"
sort={sort}
onToggle={toggle}
onToggle={sortColumn}
/>
<th className="px-4 py-2 font-medium">{t('admin.genres.aliases')}</th>
<SortHeader
label={t('admin.genres.usage')}
sortKey="showCount"
sort={sort}
onToggle={toggle}
onToggle={sortColumn}
/>
<th className="px-4 py-2 font-medium">{t('common.actions')}</th>
</tr>
@@ -177,7 +197,7 @@ export function GenresPanel() {
</td>
</tr>
)}
{sortedGenres.map((genre) => (
{visible.map((genre) => (
<tr key={genre.id} className="border-b border-border last:border-0">
<td className="px-4 py-2 text-muted-foreground">{genre.sortOrder}</td>
<td className="px-4 py-2">
@@ -212,6 +232,8 @@ export function GenresPanel() {
</table>
</div>
<Pager page={current} totalPages={totalPages} onChange={setPage} />
<Dialog open={createOpen} onOpenChange={setCreateOpen}>
<DialogContent>
<DialogHeader>