From 1e334f0ff649b49ff9860eacd9d7d2e819904013 Mon Sep 17 00:00:00 2001 From: borja Date: Thu, 16 Oct 2025 16:08:55 +0200 Subject: [PATCH] feat: group_then_due en /api/me/tasks y evitar reordenado en cliente Co-authored-by: aider (openrouter/openai/gpt-5) --- apps/web/src/routes/api/me/tasks/+server.ts | 13 +++++++++---- apps/web/src/routes/app/+page.server.ts | 15 +++------------ apps/web/src/routes/app/+page.svelte | 12 +++--------- 3 files changed, 15 insertions(+), 25 deletions(-) diff --git a/apps/web/src/routes/api/me/tasks/+server.ts b/apps/web/src/routes/api/me/tasks/+server.ts index 0624a3d..a3e59b3 100644 --- a/apps/web/src/routes/api/me/tasks/+server.ts +++ b/apps/web/src/routes/api/me/tasks/+server.ts @@ -17,6 +17,8 @@ export const GET: RequestHandler = async (event) => { const status = (url.searchParams.get('status') || 'open').trim().toLowerCase(); const page = clamp(parseInt(url.searchParams.get('page') || '1', 10) || 1, 1, 100000); const limit = clamp(parseInt(url.searchParams.get('limit') || '20', 10) || 20, 1, 100); + const orderParam = (url.searchParams.get('order') || 'due').trim().toLowerCase(); + const order = orderParam === 'group_then_due' ? 'group_then_due' : 'due'; 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; @@ -159,16 +161,19 @@ export const GET: RequestHandler = async (event) => { const total = Number(totalRow?.cnt || 0); // Items + const orderBy = + order === 'group_then_due' + ? `(t.group_id IS NULL) ASC, g.name COLLATE NOCASE ASC, CASE WHEN t.due_date IS NULL THEN 1 ELSE 0 END, t.due_date ASC, t.id ASC` + : `CASE WHEN t.due_date IS NULL THEN 1 ELSE 0 END, t.due_date ASC, t.id ASC`; + const itemsRows = db .prepare( `SELECT t.id, t.description, t.due_date, t.group_id, t.display_code FROM tasks t + LEFT JOIN groups g ON g.id = t.group_id INNER JOIN task_assignments a ON a.task_id = t.id WHERE ${whereParts.join(' AND ')} - ORDER BY - CASE WHEN t.due_date IS NULL THEN 1 ELSE 0 END, - t.due_date ASC, - t.id ASC + ORDER BY ${orderBy} LIMIT ? OFFSET ?` ) .all(...params, limit, offset) as any[]; diff --git a/apps/web/src/routes/app/+page.server.ts b/apps/web/src/routes/app/+page.server.ts index 9e45481..83df81a 100644 --- a/apps/web/src/routes/app/+page.server.ts +++ b/apps/web/src/routes/app/+page.server.ts @@ -47,8 +47,9 @@ export const load: PageServerLoad = async (event) => { const groupNames: Record = {}; try { - // Mis tareas abiertas (paginadas, orden por fecha en server) - let fetchUrl = '/api/me/tasks?limit=20'; + // Mis tareas abiertas (paginadas, orden controlado por el servidor) + const orderParamApi = order === 'group' ? 'group_then_due' : 'due'; + let fetchUrl = `/api/me/tasks?limit=20&order=${encodeURIComponent(orderParamApi)}`; fetchUrl += `&page=${encodeURIComponent(String(page))}`; const res = await event.fetch(fetchUrl, { headers: { 'cache-control': 'no-store' } }); @@ -100,16 +101,6 @@ export const load: PageServerLoad = async (event) => { } } - // Orden base por fecha para el agregado (NULL al final) - unassignedOpen.sort((a, b) => { - const ad = a.due_date, bd = b.due_date; - if (ad == null && bd == null) return a.id - b.id; - if (ad == null) return 1; - if (bd == null) return -1; - if (ad < bd) return -1; - if (ad > bd) return 1; - return a.id - b.id; - }); } catch { // Ignorar errores y dejar listas vacías } diff --git a/apps/web/src/routes/app/+page.svelte b/apps/web/src/routes/app/+page.svelte index 862b143..c624793 100644 --- a/apps/web/src/routes/app/+page.svelte +++ b/apps/web/src/routes/app/+page.svelte @@ -52,15 +52,9 @@ const groups = Array.from(map.entries()).map(([gid, tasks]) => ({ id: gid, name: gid ? (data.groupNames[gid] || gid) : "Personal", - tasks: sortByDue(tasks), + tasks })); - // Ordenar grupos por nombre (Personal al final) - groups.sort((a, b) => { - if (!a.id && !b.id) return 0; - if (!a.id) return 1; - if (!b.id) return -1; - return a.name.localeCompare(b.name, undefined, { sensitivity: "base" }); - }); + // Mantener el orden provisto por el servidor (ya ordenado alfabéticamente con "Personal" al final) return groups; } @@ -122,7 +116,7 @@ {:else}
    - {#each sortByDue(data.unassignedOpen) as t} + {#each data.unassignedOpen as t} {/each}