From d2cd2aff00f590277e7229608997f62974a5c106 Mon Sep 17 00:00:00 2001 From: borja Date: Mon, 13 Oct 2025 15:15:14 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20a=C3=B1ade=20paginaci=C3=B3n=20y=20b?= =?UTF-8?q?=C3=BAsqueda=20con=20ESCAPE=20en=20tareas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: aider (openrouter/openai/gpt-5) --- apps/web/src/routes/api/me/tasks/+server.ts | 7 ++++--- apps/web/src/routes/app/+page.server.ts | 7 ++++++- apps/web/src/routes/app/+page.svelte | 21 +++++++++++++++++++++ 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/apps/web/src/routes/api/me/tasks/+server.ts b/apps/web/src/routes/api/me/tasks/+server.ts index 1ea5850..f2e7c38 100644 --- a/apps/web/src/routes/api/me/tasks/+server.ts +++ b/apps/web/src/routes/api/me/tasks/+server.ts @@ -19,10 +19,11 @@ export const GET: RequestHandler = async (event) => { const limit = clamp(parseInt(url.searchParams.get('limit') || '20', 10) || 20, 1, 100); const dueBeforeParam = (url.searchParams.get('dueBefore') || '').trim(); const soonDaysParam = parseInt(url.searchParams.get('soonDays') || '', 10); + const soonDays = Number.isFinite(soonDaysParam) && soonDaysParam >= 0 ? Math.min(soonDaysParam, 365) : null; let dueCutoff: string | null = dueBeforeParam || null; - if (!dueCutoff && Number.isFinite(soonDaysParam) && soonDaysParam >= 0) { + if (!dueCutoff && soonDays != null) { const d = new Date(); - d.setUTCDate(d.getUTCDate() + soonDaysParam); + d.setUTCDate(d.getUTCDate() + soonDays); dueCutoff = d.toISOString().slice(0, 10); } @@ -48,7 +49,7 @@ export const GET: RequestHandler = async (event) => { params.push(userId); if (search) { - whereParts.push(`t.description LIKE ?`); + whereParts.push(`t.description LIKE ? ESCAPE '\\'`); params.push(`%${search.replace(/%/g, '\\%').replace(/_/g, '\\_')}%`); } diff --git a/apps/web/src/routes/app/+page.server.ts b/apps/web/src/routes/app/+page.server.ts index 922d61c..9320ade 100644 --- a/apps/web/src/routes/app/+page.server.ts +++ b/apps/web/src/routes/app/+page.server.ts @@ -17,24 +17,29 @@ export const load: PageServerLoad = async (event) => { display_code: number | null; assignees: string[]; }> = []; + let hasMore: boolean = false; // Filtros desde la query (?q=&soonDays=) const q = (event.url.searchParams.get('q') || '').trim(); const soonDaysStr = (event.url.searchParams.get('soonDays') || '').trim(); + const pageStr = (event.url.searchParams.get('page') || '1').trim(); + const page = Math.max(1, parseInt(pageStr, 10) || 1); try { let fetchUrl = '/api/me/tasks?limit=20'; if (q) fetchUrl += `&search=${encodeURIComponent(q)}`; if (soonDaysStr) fetchUrl += `&soonDays=${encodeURIComponent(soonDaysStr)}`; + fetchUrl += `&page=${encodeURIComponent(String(page))}`; const res = await event.fetch(fetchUrl); if (res.ok) { const json = await res.json(); tasks = Array.isArray(json?.items) ? json.items : []; + hasMore = Boolean(json?.hasMore); } } catch { // Ignorar errores y dejar lista vacía } - return { userId, tasks, q, soonDays: soonDaysStr ? Number(soonDaysStr) : null }; + return { userId, tasks, q, soonDays: soonDaysStr ? Number(soonDaysStr) : null, page, hasMore }; }; diff --git a/apps/web/src/routes/app/+page.svelte b/apps/web/src/routes/app/+page.svelte index e9eacb8..b35a0e6 100644 --- a/apps/web/src/routes/app/+page.svelte +++ b/apps/web/src/routes/app/+page.svelte @@ -11,6 +11,8 @@ }>; q?: string | null; soonDays?: number | null; + page?: number | null; + hasMore?: boolean | null; }; @@ -54,4 +56,23 @@ {/if} +{#if (data.page ?? 1) > 1 || data.hasMore} + +{/if} +

La cookie de sesión se renueva con cada visita (idle timeout).