feat: group_then_due en /api/me/tasks y evitar reordenado en cliente

Co-authored-by: aider (openrouter/openai/gpt-5) <aider@aider.chat>
webui
borja 2 weeks ago
parent 3c2f85729a
commit 1e334f0ff6

@ -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[];

@ -47,8 +47,9 @@ export const load: PageServerLoad = async (event) => {
const groupNames: Record<string, string> = {};
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
}

@ -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;
}
</script>
@ -122,7 +116,7 @@
{:else}
<Card>
<ul class="list">
{#each sortByDue(data.unassignedOpen) as t}
{#each data.unassignedOpen as t}
<TaskItem {...t} currentUserId={data.userId} groupName={t.group_id ? (data.groupNames[t.group_id] || t.group_id) : 'Personal'} />
{/each}
</ul>

Loading…
Cancel
Save