|  |  | @ -1,6 +1,7 @@ | 
			
		
	
		
		
			
				
					
					|  |  |  | <script lang="ts"> |  |  |  | <script lang="ts"> | 
			
		
	
		
		
			
				
					
					|  |  |  |   import TaskItem from '$lib/ui/data/TaskItem.svelte'; |  |  |  |   import TaskItem from '$lib/ui/data/TaskItem.svelte'; | 
			
		
	
		
		
			
				
					
					|  |  |  |   import Card from '$lib/ui/layout/Card.svelte'; |  |  |  |   import Card from '$lib/ui/layout/Card.svelte'; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |   import { onMount } from 'svelte'; | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |   type GroupItem = { |  |  |  |   type GroupItem = { | 
			
		
	
		
		
			
				
					
					|  |  |  |     id: string; |  |  |  |     id: string; | 
			
		
	
	
		
		
			
				
					|  |  | @ -25,6 +26,64 @@ | 
			
		
	
		
		
			
				
					
					|  |  |  |     if (params.unassignedFirst) sp.set('unassignedFirst', 'true'); |  |  |  |     if (params.unassignedFirst) sp.set('unassignedFirst', 'true'); | 
			
		
	
		
		
			
				
					
					|  |  |  |     return sp.toString(); |  |  |  |     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> |  |  |  | </script> | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | <svelte:head> |  |  |  | <svelte:head> | 
			
		
	
	
		
		
			
				
					|  |  | @ -53,7 +112,7 @@ | 
			
		
	
		
		
			
				
					
					|  |  |  |   </div> |  |  |  |   </div> | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |   {#each groups as g} |  |  |  |   {#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"> |  |  |  |       <summary class="group-header"> | 
			
		
	
		
		
			
				
					
					|  |  |  |         <span class="name">{g.name ?? g.id}</span> |  |  |  |         <span class="name">{g.name ?? g.id}</span> | 
			
		
	
		
		
			
				
					
					|  |  |  |         <span class="counts"> |  |  |  |         <span class="counts"> | 
			
		
	
	
		
		
			
				
					|  |  | 
 |