|
|
|
|
@ -1,8 +1,8 @@
|
|
|
|
|
<script lang="ts">
|
|
|
|
|
import Card from '$lib/ui/layout/Card.svelte';
|
|
|
|
|
import TextField from '$lib/ui/inputs/TextField.svelte';
|
|
|
|
|
import TaskItem from '$lib/ui/data/TaskItem.svelte';
|
|
|
|
|
import Pagination from '$lib/ui/layout/Pagination.svelte';
|
|
|
|
|
import Card from "$lib/ui/layout/Card.svelte";
|
|
|
|
|
import TextField from "$lib/ui/inputs/TextField.svelte";
|
|
|
|
|
import TaskItem from "$lib/ui/data/TaskItem.svelte";
|
|
|
|
|
import Pagination from "$lib/ui/layout/Pagination.svelte";
|
|
|
|
|
|
|
|
|
|
export let data: {
|
|
|
|
|
userId: string;
|
|
|
|
|
@ -31,73 +31,103 @@
|
|
|
|
|
};
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<h1 class="title">Panel</h1>
|
|
|
|
|
<p class="subtle">Sesión: <strong>{data.userId}</strong></p>
|
|
|
|
|
|
|
|
|
|
<form method="GET" action="/app" class="filters">
|
|
|
|
|
<div class="grow">
|
|
|
|
|
<TextField name="q" placeholder="Buscar tareas..." value={data.q ?? ''} />
|
|
|
|
|
</div>
|
|
|
|
|
<select name="soonDays">
|
|
|
|
|
<option value="" selected={String(data.soonDays ?? '') === ''}>Todas las fechas</option>
|
|
|
|
|
<option value="3" selected={String(data.soonDays ?? '') === '3'}>Próximos 3 días</option>
|
|
|
|
|
<option value="7" selected={String(data.soonDays ?? '') === '7'}>Próximos 7 días</option>
|
|
|
|
|
<option value="14" selected={String(data.soonDays ?? '') === '14'}>Próximos 14 días</option>
|
|
|
|
|
</select>
|
|
|
|
|
<button type="submit">Filtrar</button>
|
|
|
|
|
</form>
|
|
|
|
|
|
|
|
|
|
<h2 class="section-title">Mis tareas (abiertas)</h2>
|
|
|
|
|
{#if data.openTasks.length === 0}
|
|
|
|
|
<p>No tienes tareas abiertas.</p>
|
|
|
|
|
{:else}
|
|
|
|
|
<Card>
|
|
|
|
|
<ul class="list">
|
|
|
|
|
{#each data.openTasks as t}
|
|
|
|
|
<TaskItem {...t} currentUserId={data.userId} />
|
|
|
|
|
{/each}
|
|
|
|
|
</ul>
|
|
|
|
|
</Card>
|
|
|
|
|
<Card>
|
|
|
|
|
<ul class="list">
|
|
|
|
|
{#each data.openTasks as t}
|
|
|
|
|
<TaskItem {...t} currentUserId={data.userId} />
|
|
|
|
|
{/each}
|
|
|
|
|
</ul>
|
|
|
|
|
</Card>
|
|
|
|
|
{/if}
|
|
|
|
|
|
|
|
|
|
{#if (data.page ?? 1) > 1 || data.hasMore}
|
|
|
|
|
<Pagination
|
|
|
|
|
prevHref={(data.page ?? 1) > 1
|
|
|
|
|
? `/app?${new URLSearchParams({ q: data.q ?? '', soonDays: data.soonDays != null ? String(data.soonDays) : '', page: String((data.page ?? 1) - 1) }).toString()}`
|
|
|
|
|
: null}
|
|
|
|
|
nextHref={data.hasMore
|
|
|
|
|
? `/app?${new URLSearchParams({ q: data.q ?? '', soonDays: data.soonDays != null ? String(data.soonDays) : '', page: String((data.page ?? 1) + 1) }).toString()}`
|
|
|
|
|
: null}
|
|
|
|
|
/>
|
|
|
|
|
<Pagination
|
|
|
|
|
prevHref={(data.page ?? 1) > 1
|
|
|
|
|
? `/app?${new URLSearchParams({ q: data.q ?? "", soonDays: data.soonDays != null ? String(data.soonDays) : "", page: String((data.page ?? 1) - 1) }).toString()}`
|
|
|
|
|
: null}
|
|
|
|
|
nextHref={data.hasMore
|
|
|
|
|
? `/app?${new URLSearchParams({ q: data.q ?? "", soonDays: data.soonDays != null ? String(data.soonDays) : "", page: String((data.page ?? 1) + 1) }).toString()}`
|
|
|
|
|
: null}
|
|
|
|
|
/>
|
|
|
|
|
{/if}
|
|
|
|
|
|
|
|
|
|
<h2 class="section-title">Completadas (últimas 24 h)</h2>
|
|
|
|
|
{#if data.recentTasks.length === 0}
|
|
|
|
|
<p>No hay tareas completadas recientemente.</p>
|
|
|
|
|
<p>No hay tareas completadas recientemente.</p>
|
|
|
|
|
{:else}
|
|
|
|
|
<Card>
|
|
|
|
|
<ul class="list">
|
|
|
|
|
{#each data.recentTasks as t}
|
|
|
|
|
<TaskItem {...t} currentUserId={data.userId} completed={true} completed_at={t.completed_at ?? null} />
|
|
|
|
|
{/each}
|
|
|
|
|
</ul>
|
|
|
|
|
</Card>
|
|
|
|
|
<Card>
|
|
|
|
|
<ul class="list">
|
|
|
|
|
{#each data.recentTasks as t}
|
|
|
|
|
<TaskItem
|
|
|
|
|
{...t}
|
|
|
|
|
currentUserId={data.userId}
|
|
|
|
|
completed={true}
|
|
|
|
|
completed_at={t.completed_at ?? null}
|
|
|
|
|
/>
|
|
|
|
|
{/each}
|
|
|
|
|
</ul>
|
|
|
|
|
</Card>
|
|
|
|
|
{/if}
|
|
|
|
|
|
|
|
|
|
<p class="footnote">La cookie de sesión se renueva con cada visita (idle timeout).</p>
|
|
|
|
|
<form method="GET" action="/app" class="filters">
|
|
|
|
|
<div class="grow">
|
|
|
|
|
<TextField name="q" placeholder="Buscar tareas..." value={data.q ?? ""} />
|
|
|
|
|
</div>
|
|
|
|
|
<select name="soonDays">
|
|
|
|
|
<option value="" selected={String(data.soonDays ?? "") === ""}
|
|
|
|
|
>Todas las fechas</option
|
|
|
|
|
>
|
|
|
|
|
<option value="3" selected={String(data.soonDays ?? "") === "3"}
|
|
|
|
|
>Próximos 3 días</option
|
|
|
|
|
>
|
|
|
|
|
<option value="7" selected={String(data.soonDays ?? "") === "7"}
|
|
|
|
|
>Próximos 7 días</option
|
|
|
|
|
>
|
|
|
|
|
<option value="14" selected={String(data.soonDays ?? "") === "14"}
|
|
|
|
|
>Próximos 14 días</option
|
|
|
|
|
>
|
|
|
|
|
</select>
|
|
|
|
|
<button type="submit">Filtrar</button>
|
|
|
|
|
</form>
|
|
|
|
|
<p class="footnote">
|
|
|
|
|
La cookie de sesión se renueva con cada visita (idle timeout).
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
<style>
|
|
|
|
|
.title { margin-bottom: .5rem; }
|
|
|
|
|
.subtle { color: var(--color-text-muted); margin: 0 0 1rem 0; }
|
|
|
|
|
.filters {
|
|
|
|
|
margin: 0 0 1rem 0;
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
align-items: center;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
}
|
|
|
|
|
.grow { flex: 1 1 260px; min-width: 200px; }
|
|
|
|
|
.section-title { margin: .5rem 0; }
|
|
|
|
|
.list { margin: 0; padding: 0; list-style: none; }
|
|
|
|
|
.footnote { margin-top: .75rem; color: var(--color-text-muted); }
|
|
|
|
|
.title {
|
|
|
|
|
margin-bottom: 0.5rem;
|
|
|
|
|
}
|
|
|
|
|
.subtle {
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
margin: 0 0 1rem 0;
|
|
|
|
|
}
|
|
|
|
|
.filters {
|
|
|
|
|
margin: 0 0 1rem 0;
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
align-items: center;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
}
|
|
|
|
|
.grow {
|
|
|
|
|
flex: 1 1 260px;
|
|
|
|
|
min-width: 200px;
|
|
|
|
|
}
|
|
|
|
|
.section-title {
|
|
|
|
|
margin: 0.5rem 0;
|
|
|
|
|
}
|
|
|
|
|
.list {
|
|
|
|
|
margin: 0;
|
|
|
|
|
padding: 0;
|
|
|
|
|
list-style: none;
|
|
|
|
|
}
|
|
|
|
|
.footnote {
|
|
|
|
|
margin-top: 0.75rem;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
|