|
|
|
|
@ -1,6 +1,7 @@
|
|
|
|
|
<script lang="ts">
|
|
|
|
|
import TaskItem from '$lib/ui/data/TaskItem.svelte';
|
|
|
|
|
import Card from '$lib/ui/layout/Card.svelte';
|
|
|
|
|
import { onMount } from 'svelte';
|
|
|
|
|
|
|
|
|
|
type GroupItem = {
|
|
|
|
|
id: string;
|
|
|
|
|
@ -25,6 +26,64 @@
|
|
|
|
|
if (params.unassignedFirst) sp.set('unassignedFirst', 'true');
|
|
|
|
|
return sp.toString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const storageKey = `groupsCollapsed:v1:${data.userId ?? 'anon'}`;
|
|
|
|
|
let collapsed: Record<string, boolean> = {};
|
|
|
|
|
|
|
|
|
|
function hasTasks(groupId: string): boolean {
|
|
|
|
|
const arr = itemsByGroup[groupId] || [];
|
|
|
|
|
return Array.isArray(arr) && arr.length > 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function defaultCollapsedFor(groupId: string): boolean {
|
|
|
|
|
// Por defecto, colapsado si no tiene tareas abiertas
|
|
|
|
|
return !hasTasks(groupId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function isOpen(groupId: string): boolean {
|
|
|
|
|
const v = collapsed[groupId];
|
|
|
|
|
if (typeof v === 'boolean') return !v;
|
|
|
|
|
return !defaultCollapsedFor(groupId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function saveCollapsed() {
|
|
|
|
|
try {
|
|
|
|
|
const currentIds = new Set(groups.map(g => g.id));
|
|
|
|
|
const pruned: Record<string, boolean> = {};
|
|
|
|
|
for (const id of Object.keys(collapsed)) {
|
|
|
|
|
if (currentIds.has(id)) pruned[id] = !!collapsed[id];
|
|
|
|
|
}
|
|
|
|
|
localStorage.setItem(storageKey, JSON.stringify(pruned));
|
|
|
|
|
} catch {}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function handleToggle(groupId: string, e: Event) {
|
|
|
|
|
const open = (e.currentTarget as HTMLDetailsElement).open;
|
|
|
|
|
collapsed = { ...collapsed, [groupId]: !open };
|
|
|
|
|
saveCollapsed();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onMount(() => {
|
|
|
|
|
try {
|
|
|
|
|
const raw = localStorage.getItem(storageKey);
|
|
|
|
|
const saved = raw ? JSON.parse(raw) : {};
|
|
|
|
|
const map: Record<string, boolean> = {};
|
|
|
|
|
const currentIds = new Set(groups.map(g => g.id));
|
|
|
|
|
for (const g of groups) {
|
|
|
|
|
map[g.id] = typeof saved?.[g.id] === 'boolean' ? !!saved[g.id] : defaultCollapsedFor(g.id);
|
|
|
|
|
}
|
|
|
|
|
// Limpieza de claves obsoletas en storage
|
|
|
|
|
const cleaned: Record<string, boolean> = {};
|
|
|
|
|
for (const k of Object.keys(saved || {})) {
|
|
|
|
|
if (currentIds.has(k)) cleaned[k] = !!saved[k];
|
|
|
|
|
}
|
|
|
|
|
collapsed = map;
|
|
|
|
|
localStorage.setItem(storageKey, JSON.stringify(cleaned || map));
|
|
|
|
|
} catch {
|
|
|
|
|
// si falla, dejamos los defaults (basados en tareas)
|
|
|
|
|
collapsed = {};
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<svelte:head>
|
|
|
|
|
@ -53,7 +112,7 @@
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{#each groups as g}
|
|
|
|
|
<details open class="group">
|
|
|
|
|
<details class="group" open={isOpen(g.id)} on:toggle={(e) => handleToggle(g.id, e)}>
|
|
|
|
|
<summary class="group-header">
|
|
|
|
|
<span class="name">{g.name ?? g.id}</span>
|
|
|
|
|
<span class="counts">
|
|
|
|
|
|