Foro: paginación de temas (20/pág) y de posts (15/pág)

- lib/forum.ts: countTopics/countPosts + offset/limit en getTopics/getPosts
  (mismos límites que Django: FORUM_TOPICS_PER_PAGE=20, FORUM_POSTS_PER_PAGE=15).
- components/Pagination.tsx: paginación reutilizable (ventana ±2, primera/última,
  prev/next) con enlaces ?page=N; no renderiza si solo hay una página.
- Foro y tema aceptan ?page=N (clamp al rango válido).
- El formulario de respuesta solo aparece en la última página; enlace a ella si no.
- i18n: namespace Common (prev/next/page) + replyOnLastPage.

Build OK. Sin datos de foro en acore_web (estado vacío correcto).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-13 00:32:16 +00:00
parent 1de1002938
commit 36a4354ff7
6 changed files with 143 additions and 11 deletions
+28 -4
View File
@@ -104,12 +104,24 @@ export async function getForum(id: number): Promise<{ name: string; description:
}
}
export async function getTopics(forumId: number): Promise<Topic[]> {
export async function countTopics(forumId: number): Promise<number> {
try {
const [rows] = await db(DB.web).query<RowDataPacket[]>(
'SELECT id, name, poster, created, sticky, locked, views FROM forum_topics WHERE forum_id = ? AND deleted = 0 ORDER BY sticky DESC, updated_at DESC, id DESC',
'SELECT COUNT(*) AS n FROM forum_topics WHERE forum_id = ? AND deleted = 0',
[forumId],
)
return Number(rows[0]?.n ?? 0)
} catch {
return 0
}
}
export async function getTopics(forumId: number, offset = 0, limit = 20): Promise<Topic[]> {
try {
const [rows] = await db(DB.web).query<RowDataPacket[]>(
'SELECT id, name, poster, created, sticky, locked, views FROM forum_topics WHERE forum_id = ? AND deleted = 0 ORDER BY sticky DESC, updated_at DESC, id DESC LIMIT ? OFFSET ?',
[forumId, limit, offset],
)
return rows.map((r) => ({
id: r.id,
name: r.name,
@@ -146,12 +158,24 @@ export async function getTopic(id: number): Promise<TopicFull | null> {
}
}
export async function getPosts(topicId: number): Promise<Post[]> {
export async function countPosts(topicId: number): Promise<number> {
try {
const [rows] = await db(DB.web).query<RowDataPacket[]>(
'SELECT id, poster, poster_id, text, time, created_at, updated_at FROM forum_posts WHERE topic_id = ? AND deleted = 0 ORDER BY time ASC, id ASC',
'SELECT COUNT(*) AS n FROM forum_posts WHERE topic_id = ? AND deleted = 0',
[topicId],
)
return Number(rows[0]?.n ?? 0)
} catch {
return 0
}
}
export async function getPosts(topicId: number, offset = 0, limit = 15): Promise<Post[]> {
try {
const [rows] = await db(DB.web).query<RowDataPacket[]>(
'SELECT id, poster, poster_id, text, time, created_at, updated_at FROM forum_posts WHERE topic_id = ? AND deleted = 0 ORDER BY time ASC, id ASC LIMIT ? OFFSET ?',
[topicId, limit, offset],
)
return rows.map((r) => ({
id: r.id,
poster: r.poster,