Add performance optimizations and component refactors
- Cache read-heavy public API routes via redisCache (leaderboard, values, shop, articles, photos, guilds, teams, staff, users, home, radio, badges) - Add single-flight and bounded-memory cache layer with unit tests - Parallelize independent DB queries on search, rares, shop, staff, polls and profile pages - Push radio points leaderboard aggregation to SQL with a LIMIT - Split studio-client and import-furni-client into focused modules - Clean up next.config.ts
This commit is contained in:
1 parent
54f2bc5e0c
commit
f285a7cd98
42 files changed
+1616
-1205
No files matched your search
@@ -0,0 +1,89 @@
|
||||
"use client";
|
||||
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import type { Meta } from "./import-furni-types";
|
||||
|
||||
export function Pagination({
|
||||
meta,
|
||||
loading,
|
||||
onPageChange,
|
||||
}: {
|
||||
meta: Meta | null;
|
||||
loading: boolean;
|
||||
onPageChange: (page: number) => void;
|
||||
}) {
|
||||
if (!meta || meta.lastPage <= 1) return null;
|
||||
const nodes: ReactNode[] = [];
|
||||
if (meta.currentPage > 1) {
|
||||
nodes.push(
|
||||
<Button
|
||||
key="prev"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => onPageChange(meta.currentPage - 1)}
|
||||
disabled={loading}
|
||||
className="text-xs"
|
||||
>
|
||||
← Prev
|
||||
</Button>,
|
||||
);
|
||||
}
|
||||
for (let p = 1; p <= meta.lastPage; p++) {
|
||||
if (p === meta.currentPage) {
|
||||
nodes.push(
|
||||
<Button
|
||||
key={p}
|
||||
variant="default"
|
||||
size="sm"
|
||||
className="text-xs min-w-[32px]"
|
||||
>
|
||||
{p}
|
||||
</Button>,
|
||||
);
|
||||
} else if (
|
||||
Math.abs(p - meta.currentPage) <= 2 ||
|
||||
p === 1 ||
|
||||
p === meta.lastPage
|
||||
) {
|
||||
nodes.push(
|
||||
<Button
|
||||
key={p}
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => onPageChange(p)}
|
||||
disabled={loading}
|
||||
className="text-xs min-w-[32px]"
|
||||
>
|
||||
{p}
|
||||
</Button>,
|
||||
);
|
||||
} else if (Math.abs(p - meta.currentPage) === 3) {
|
||||
nodes.push(
|
||||
<span key={`dots-${p}`} className="text-muted-foreground text-xs px-1">
|
||||
…
|
||||
</span>,
|
||||
);
|
||||
}
|
||||
}
|
||||
if (meta.currentPage < meta.lastPage) {
|
||||
nodes.push(
|
||||
<Button
|
||||
key="next"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => onPageChange(meta.currentPage + 1)}
|
||||
disabled={loading}
|
||||
className="text-xs"
|
||||
>
|
||||
Next →
|
||||
</Button>,
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="flex items-center justify-center gap-1 mt-4 flex-wrap">
|
||||
{nodes}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user