import { TaskService } from '../../../tasks/service'; import { GroupSyncService } from '../../group-sync'; import { ContactsService } from '../../contacts'; import { ICONS } from '../../../utils/icons'; import { codeId, formatDDMM, bold, italic } from '../../../utils/formatting'; import { SCOPE_ALIASES, todayYMD } from '../shared'; type Ctx = { sender: string; groupId: string; message: string; }; type Msg = { recipient: string; message: string; mentions?: string[]; }; export async function handleVer(context: Ctx): Promise { const trimmed = (context.message || '').trim(); const tokens = trimmed.split(/\s+/); const rawAction = (tokens[1] || '').toLowerCase(); const scopeRaw = (tokens[2] || '').toLowerCase(); const scope = scopeRaw ? (SCOPE_ALIASES[scopeRaw] || scopeRaw) : ((rawAction === 'mias' || rawAction === 'mías') ? 'mis' : ((rawAction === 'todas' || rawAction === 'todos') ? 'todos' : 'todos')); const LIMIT = 10; const today = todayYMD(); if (scope === 'todos') { const sections: string[] = []; // Encabezado fijo para la sección de tareas del usuario sections.push(bold('Tus tareas')); // Tus tareas (mis) const myItems = TaskService.listUserPending(context.sender, LIMIT); if (myItems.length > 0) { // Agrupar por grupo como en "ver mis" const byGroup = new Map(); for (const t of myItems) { const key = t.group_id || '(sin grupo)'; const arr = byGroup.get(key) || []; arr.push(t); byGroup.set(key, arr); } for (const [groupId, arr] of byGroup.entries()) { const groupName = (groupId && GroupSyncService.activeGroupsCache.get(groupId)) || (groupId && groupId !== '(sin grupo)' ? groupId : 'Sin grupo'); sections.push(groupName); const rendered = await Promise.all(arr.map(async (t) => { const names = await Promise.all( (t.assignees || []).map(async (uid) => (await ContactsService.getDisplayName(uid)) || uid) ); const owner = (t.assignees?.length || 0) === 0 ? `${ICONS.unassigned} sin responsable` : `${t.assignees!.length > 1 ? '👥' : '👤'} ${names.join(', ')}`; const isOverdue = t.due_date ? t.due_date < today : false; const datePart = t.due_date ? ` — ${isOverdue ? `${ICONS.warn} ` : ''}${ICONS.date} ${formatDDMM(t.due_date)}` : ''; const dc = (t as any)?.display_code as number | undefined; return `- ${codeId(t.id, dc)} ${t.description || '(sin descripción)'}${datePart} — ${owner}`; })); sections.push(...rendered); sections.push(''); } // Quitar línea en blanco final si procede if (sections.length > 0 && sections[sections.length - 1] === '') { sections.pop(); } const totalMy = TaskService.countUserPending(context.sender); if (totalMy > myItems.length) { sections.push(`… y ${totalMy - myItems.length} más`); } } else { sections.push(italic('_No tienes tareas pendientes._')); } // En DM: usar membresía real (snapshot fresca) para incluir "sin responsable" por grupo const memberGroups = GroupSyncService.getFreshMemberGroupsForUser(context.sender); if (memberGroups.length > 0) { const perGroup = TaskService.listUnassignedByGroups(memberGroups, LIMIT); for (const gid of perGroup.keys()) { const unassigned = perGroup.get(gid)!; const groupName = (gid && GroupSyncService.activeGroupsCache.get(gid)) || gid; if (unassigned.length > 0) { if (sections.length && sections[sections.length - 1] !== '') sections.push(''); sections.push(`${groupName} — Sin responsable`); const renderedUnassigned = unassigned.map((t) => { const isOverdue = t.due_date ? t.due_date < today : false; const datePart = t.due_date ? ` — ${isOverdue ? `${ICONS.warn} ` : ''}${ICONS.date} ${formatDDMM(t.due_date)}` : ''; const dc = (t as any)?.display_code as number | undefined; return `- ${codeId(t.id, dc)} ${t.description || '(sin descripción)'}${datePart} — ${ICONS.unassigned}`; }); sections.push(...renderedUnassigned); const totalUnassigned = TaskService.countGroupUnassigned(gid); if (totalUnassigned > unassigned.length) { sections.push(`… y ${totalUnassigned - unassigned.length} más`); } } } } else { // Si no hay snapshot fresca de membresía, nota instructiva sections.push('ℹ️ Para ver tareas sin responsable, escribe por privado `/t todas` o usa `/t web`.'); } return [{ recipient: context.sender, message: sections.join('\n') }]; } // Ver mis const items = TaskService.listUserPending(context.sender, LIMIT); if (items.length === 0) { return [{ recipient: context.sender, message: italic('No tienes tareas pendientes.') }]; } const total = TaskService.countUserPending(context.sender); // Agrupar por grupo const byGroup = new Map(); for (const t of items) { const key = t.group_id || '(sin grupo)'; const arr = byGroup.get(key) || []; arr.push(t); byGroup.set(key, arr); } const sections: string[] = [bold('Tus tareas')]; for (const [groupId, arr] of byGroup.entries()) { const groupName = (groupId && GroupSyncService.activeGroupsCache.get(groupId)) || (groupId && groupId !== '(sin grupo)' ? groupId : 'Sin grupo'); sections.push(groupName); const rendered = await Promise.all(arr.map(async (t) => { const names = await Promise.all( (t.assignees || []).map(async (uid) => (await ContactsService.getDisplayName(uid)) || uid) ); const owner = (t.assignees?.length || 0) === 0 ? `${ICONS.unassigned}` : `${t.assignees!.length > 1 ? '👥' : '👤'} ${names.join(', ')}`; const isOverdue = t.due_date ? t.due_date < today : false; const datePart = t.due_date ? ` — ${isOverdue ? `${ICONS.warn} ` : ''}${ICONS.date} ${formatDDMM(t.due_date)}` : ''; const dc = (t as any)?.display_code as number | undefined; return `- ${codeId(t.id, dc)} ${t.description || '(sin descripción)'}${datePart} — ${owner}`; })); sections.push(...rendered); sections.push(''); } // Quitar línea en blanco final si procede if (sections.length > 0 && sections[sections.length - 1] === '') { sections.pop(); } if (total > items.length) { sections.push(`… y ${total - items.length} más`); } return [{ recipient: context.sender, message: sections.join('\n') }]; }