fix : show 25 item per page in admin glossary
This commit is contained in:
@@ -15,6 +15,7 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/com
|
|||||||
import { FuzzyCombobox, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem, ComboboxList } from "@/components/ui/combobox";
|
import { FuzzyCombobox, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem, ComboboxList } from "@/components/ui/combobox";
|
||||||
import { Field, FieldGroup, FieldLabel } from "@/components/ui/field";
|
import { Field, FieldGroup, FieldLabel } from "@/components/ui/field";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious } from "@/components/ui/pagination";
|
||||||
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||||
import { Spinner } from "@/components/ui/spinner";
|
import { Spinner } from "@/components/ui/spinner";
|
||||||
import type { GlossaryKind } from "@/lib/glossary";
|
import type { GlossaryKind } from "@/lib/glossary";
|
||||||
@@ -24,6 +25,15 @@ import { cn } from "@/lib/utils";
|
|||||||
type CatalogItem = { key: string; name: string; imageUrl: string; rarity?: number | null };
|
type CatalogItem = { key: string; name: string; imageUrl: string; rarity?: number | null };
|
||||||
type Alias = { id: string; kind: string; catalogKey: string; alias: string };
|
type Alias = { id: string; kind: string; catalogKey: string; alias: string };
|
||||||
|
|
||||||
|
const PAGE_SIZE = 25;
|
||||||
|
|
||||||
|
function paginationItems(currentPage: number, totalPages: number) {
|
||||||
|
if (totalPages <= 7) return Array.from({ length: totalPages }, (_, index) => index + 1);
|
||||||
|
if (currentPage <= 4) return [1, 2, 3, 4, 5, "end-ellipsis", totalPages] as const;
|
||||||
|
if (currentPage >= totalPages - 3) return [1, "start-ellipsis", totalPages - 4, totalPages - 3, totalPages - 2, totalPages - 1, totalPages] as const;
|
||||||
|
return [1, "start-ellipsis", currentPage - 1, currentPage, currentPage + 1, "end-ellipsis", totalPages] as const;
|
||||||
|
}
|
||||||
|
|
||||||
const kindLabels: Record<GlossaryKind, string> = {
|
const kindLabels: Record<GlossaryKind, string> = {
|
||||||
character: "Character",
|
character: "Character",
|
||||||
weapon: "Weapon",
|
weapon: "Weapon",
|
||||||
@@ -43,6 +53,7 @@ export function GlossaryManager({
|
|||||||
const [item, setItem] = useState<CatalogItem | null>(null);
|
const [item, setItem] = useState<CatalogItem | null>(null);
|
||||||
const [alias, setAlias] = useState("");
|
const [alias, setAlias] = useState("");
|
||||||
const [query, setQuery] = useState("");
|
const [query, setQuery] = useState("");
|
||||||
|
const [page, setPage] = useState(1);
|
||||||
const itemsByKey = useMemo(() => new Map(
|
const itemsByKey = useMemo(() => new Map(
|
||||||
Object.entries(catalogs).flatMap(([catalogKind, items]) =>
|
Object.entries(catalogs).flatMap(([catalogKind, items]) =>
|
||||||
items.map((catalogItem) => [`${catalogKind}:${catalogItem.key}`, catalogItem] as const),
|
items.map((catalogItem) => [`${catalogKind}:${catalogItem.key}`, catalogItem] as const),
|
||||||
@@ -52,6 +63,10 @@ export function GlossaryManager({
|
|||||||
(entry) => `${entry.alias} ${itemsByKey.get(`${entry.kind}:${entry.catalogKey}`)?.name ?? entry.catalogKey}`,
|
(entry) => `${entry.alias} ${itemsByKey.get(`${entry.kind}:${entry.catalogKey}`)?.name ?? entry.catalogKey}`,
|
||||||
(entry) => [entry.alias, itemsByKey.get(`${entry.kind}:${entry.catalogKey}`)?.name ?? ""]);
|
(entry) => [entry.alias, itemsByKey.get(`${entry.kind}:${entry.catalogKey}`)?.name ?? ""]);
|
||||||
const visibleAliases = search.items;
|
const visibleAliases = search.items;
|
||||||
|
const totalPages = Math.max(1, Math.ceil(visibleAliases.length / PAGE_SIZE));
|
||||||
|
const currentPage = Math.min(page, totalPages);
|
||||||
|
const paginatedAliases = visibleAliases.slice((currentPage - 1) * PAGE_SIZE, currentPage * PAGE_SIZE);
|
||||||
|
const pages = paginationItems(currentPage, totalPages);
|
||||||
|
|
||||||
const submit = () => startTransition(async () => {
|
const submit = () => startTransition(async () => {
|
||||||
const result = await addGlossaryAlias({ kind, catalogKey: item?.key, alias });
|
const result = await addGlossaryAlias({ kind, catalogKey: item?.key, alias });
|
||||||
@@ -142,11 +157,14 @@ export function GlossaryManager({
|
|||||||
<FieldLabel htmlFor="glossary-search">ค้นหา</FieldLabel>
|
<FieldLabel htmlFor="glossary-search">ค้นหา</FieldLabel>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<SearchIcon className="pointer-events-none absolute top-1/2 left-3 size-4 -translate-y-1/2 text-muted-foreground" />
|
<SearchIcon className="pointer-events-none absolute top-1/2 left-3 size-4 -translate-y-1/2 text-muted-foreground" />
|
||||||
<Input id="glossary-search" className="pl-9" value={query} onChange={(event) => setQuery(event.target.value)} />
|
<Input id="glossary-search" className="pl-9" value={query} onChange={(event) => {
|
||||||
|
setQuery(event.target.value);
|
||||||
|
setPage(1);
|
||||||
|
}} />
|
||||||
</div>
|
</div>
|
||||||
</Field>
|
</Field>
|
||||||
|
|
||||||
{visibleAliases.map((entry) => {
|
{paginatedAliases.map((entry) => {
|
||||||
const catalogItem = itemsByKey.get(`${entry.kind}:${entry.catalogKey}`);
|
const catalogItem = itemsByKey.get(`${entry.kind}:${entry.catalogKey}`);
|
||||||
const name = catalogItem?.name;
|
const name = catalogItem?.name;
|
||||||
return (
|
return (
|
||||||
@@ -175,6 +193,64 @@ export function GlossaryManager({
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
||||||
|
{totalPages > 1 ? (
|
||||||
|
<Pagination className="pt-2">
|
||||||
|
<PaginationContent className="grid w-full grid-cols-[1fr_auto_1fr]">
|
||||||
|
<PaginationItem className="justify-self-start">
|
||||||
|
<PaginationPrevious
|
||||||
|
href="#"
|
||||||
|
text="ก่อนหน้า"
|
||||||
|
aria-disabled={currentPage === 1}
|
||||||
|
tabIndex={currentPage === 1 ? -1 : undefined}
|
||||||
|
className={cn(currentPage === 1 && "pointer-events-none opacity-50")}
|
||||||
|
onClick={(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
setPage(currentPage - 1);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</PaginationItem>
|
||||||
|
<PaginationItem>
|
||||||
|
<ul className="flex items-center gap-0.5">
|
||||||
|
{pages.map((pageItem) => (
|
||||||
|
typeof pageItem === "number" ? (
|
||||||
|
<PaginationItem key={pageItem}>
|
||||||
|
<PaginationLink
|
||||||
|
href="#"
|
||||||
|
isActive={pageItem === currentPage}
|
||||||
|
aria-label={`ไปหน้า ${pageItem}`}
|
||||||
|
onClick={(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
setPage(pageItem);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{pageItem}
|
||||||
|
</PaginationLink>
|
||||||
|
</PaginationItem>
|
||||||
|
) : (
|
||||||
|
<PaginationItem key={pageItem}>
|
||||||
|
<PaginationEllipsis />
|
||||||
|
</PaginationItem>
|
||||||
|
)
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</PaginationItem>
|
||||||
|
<PaginationItem className="justify-self-end">
|
||||||
|
<PaginationNext
|
||||||
|
href="#"
|
||||||
|
text="ถัดไป"
|
||||||
|
aria-disabled={currentPage === totalPages}
|
||||||
|
tabIndex={currentPage === totalPages ? -1 : undefined}
|
||||||
|
className={cn(currentPage === totalPages && "pointer-events-none opacity-50")}
|
||||||
|
onClick={(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
setPage(currentPage + 1);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</PaginationItem>
|
||||||
|
</PaginationContent>
|
||||||
|
</Pagination>
|
||||||
|
) : null}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user