From b7e920dcaf39791e8f119688ba3ef0def42ed2ac Mon Sep 17 00:00:00 2001 From: AI Bot Date: Thu, 3 Sep 2026 11:51:47 -0500 Subject: [PATCH] feat(feed): control por identificador de whatsapp para excluir mensajes publicados y descartados y avanzar lista --- .../src/app/admin/feed-whatsapp/page.tsx | 109 +++++++++-- .../src/app/api/whatsapp-feed/route.ts | 182 ++++++++++++++---- dripzone-app/src/lib/server-appwrite.ts | 32 ++- dripzone-app/src/types/index.ts | 3 + scripts/send_apk_to_whatsapp.js | 2 +- scripts/send_whatsapp_delivery.py | 74 +++++++ zapply-app/src/app/api/whatsapp-feed/route.ts | 118 ++++++++++-- zapply-app/src/types/index.ts | 3 + 8 files changed, 439 insertions(+), 84 deletions(-) create mode 100644 scripts/send_whatsapp_delivery.py diff --git a/dripzone-app/src/app/admin/feed-whatsapp/page.tsx b/dripzone-app/src/app/admin/feed-whatsapp/page.tsx index f08a1a8..3efac13 100644 --- a/dripzone-app/src/app/admin/feed-whatsapp/page.tsx +++ b/dripzone-app/src/app/admin/feed-whatsapp/page.tsx @@ -44,7 +44,7 @@ export default function SupplierFeedPage() { const [monitoredGroups, setMonitoredGroups] = useState([]); const [existingProducts, setExistingProducts] = useState([]); const [selectedGroupFilter, setSelectedGroupFilter] = useState('all'); - const [tabStatus, setTabStatus] = useState<'all' | 'pending' | 'published' | 'discarded'>('all'); + const [tabStatus, setTabStatus] = useState<'pending' | 'published' | 'discarded' | 'all'>('pending'); const [isLoading, setIsLoading] = useState(false); const [isSyncingLive, setIsSyncingLive] = useState(false); const [actionMessage, setActionMessage] = useState(null); @@ -77,7 +77,7 @@ export default function SupplierFeedPage() { // Target brand selector for publishing const [targetBrand, setTargetBrand] = useState('almeira'); - // Cargar grupos monitoreados, productos existentes y posts en vivo + // Cargar grupos monitoreados, productos existentes y posts en vivo (por defecto PENDIENTES) const loadInitialData = async () => { setIsLoading(true); try { @@ -95,8 +95,8 @@ export default function SupplierFeedPage() { setExistingProducts(prodData.products); } - // 3. Posts en vivo desde los grupos de WhatsApp (todos los mensajes: leídos y sin leer) - const feedRes = await fetch('/api/whatsapp-feed?fetchLive=true&limit=60'); + // 3. Posts en vivo desde los grupos de WhatsApp (iniciando con PENDIENTES para que la lista avance) + const feedRes = await fetch('/api/whatsapp-feed?fetchLive=true&limit=60&status=pending'); const feedData = await feedRes.json(); if (feedData.success && Array.isArray(feedData.posts)) { setPosts(feedData.posts); @@ -120,13 +120,13 @@ export default function SupplierFeedPage() { try { const url = groupJid !== 'all' - ? `/api/whatsapp-feed?fetchLive=true&groupJid=${encodeURIComponent(groupJid)}` - : '/api/whatsapp-feed?fetchLive=true&limit=60'; + ? `/api/whatsapp-feed?fetchLive=true&groupJid=${encodeURIComponent(groupJid)}&status=${tabStatus}` + : `/api/whatsapp-feed?fetchLive=true&limit=60&status=${tabStatus}`; const res = await fetch(url); const data = await res.json(); if (data.success && Array.isArray(data.posts)) { setPosts(data.posts); - setActionMessage(`✅ Se cargaron ${data.totalFound || data.posts.length} mensajes (leídos y sin leer) del grupo.`); + setActionMessage(`✅ Se cargaron ${data.totalFound || data.posts.length} mensajes.`); } } catch (err) { console.error(err); @@ -143,15 +143,15 @@ export default function SupplierFeedPage() { try { const url = selectedGroupFilter !== 'all' - ? `/api/whatsapp-feed?fetchLive=true&groupJid=${encodeURIComponent(selectedGroupFilter)}` - : '/api/whatsapp-feed?fetchLive=true&limit=60'; + ? `/api/whatsapp-feed?fetchLive=true&groupJid=${encodeURIComponent(selectedGroupFilter)}&status=${tabStatus}` + : `/api/whatsapp-feed?fetchLive=true&limit=60&status=${tabStatus}`; const res = await fetch(url); const data = await res.json(); if (data.success && Array.isArray(data.posts)) { setPosts(data.posts); setActionMessage( - `✅ ¡Sincronización completa! Se escanearon ${data.totalFound || data.posts.length} mensajes de WhatsApp (leídos y sin leer) y se sincronizaron con Appwrite.` + `✅ ¡Sincronización completa! Se escanearon los mensajes de WhatsApp y se omitieron los ya publicados y descartados.` ); } else { setActionMessage('⚠️ No se encontraron mensajes nuevos en los grupos seleccionados.'); @@ -284,7 +284,7 @@ export default function SupplierFeedPage() { } }; - // Descartar mensaje + // Descartar mensaje individual const handleDiscardPost = async (post: SupplierPost) => { try { await fetch('/api/whatsapp-feed', { @@ -300,8 +300,45 @@ export default function SupplierFeedPage() { next.delete(post.id); return next; }); + setActionMessage(`🗑️ Mensaje #${post.whatsappMessageId || post.id.replace('post-', '')} descartado y archivado. No se volverá a mostrar en pendientes.`); } catch (e) { console.error(e); + setActionMessage('❌ Error al descartar mensaje.'); + } + }; + + // Descarte masivo de mensajes seleccionados + const handleBulkDiscard = async () => { + const selectedPosts = posts.filter( + (p) => selectedPostIds.has(p.id) && p.status !== 'discarded' + ); + if (selectedPosts.length === 0) return; + + setIsLoading(true); + try { + const res = await fetch('/api/whatsapp-feed', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + action: 'bulk-discard', + posts: selectedPosts, + }), + }); + const data = await res.json(); + if (data.success) { + setPosts((prev) => + prev.map((p) => (selectedPostIds.has(p.id) ? { ...p, status: 'discarded' } : p)) + ); + setSelectedPostIds(new Set()); + setActionMessage( + `🗑️ Se han descartado ${selectedPosts.length} mensajes. No volverán a aparecer en la lista de pendientes.` + ); + } + } catch (e) { + console.error(e); + setActionMessage('❌ Error en descarte masivo.'); + } finally { + setIsLoading(false); } }; @@ -315,6 +352,24 @@ export default function SupplierFeedPage() { }); }; + // Cambiar pestaña y actualizar vista + const handleTabChange = async (status: 'pending' | 'published' | 'discarded' | 'all') => { + setTabStatus(status); + setSelectedPostIds(new Set()); + setCurrentPage(1); + try { + const url = + selectedGroupFilter !== 'all' + ? `/api/whatsapp-feed?fetchLive=true&groupJid=${encodeURIComponent(selectedGroupFilter)}&status=${status}` + : `/api/whatsapp-feed?fetchLive=true&limit=60&status=${status}`; + const res = await fetch(url); + const data = await res.json(); + if (data.success && Array.isArray(data.posts)) { + setPosts(data.posts); + } + } catch (_) {} + }; + // Filtrado de posts por estado y grupo const filteredPosts = useMemo(() => { return posts.filter((p) => { @@ -445,7 +500,7 @@ export default function SupplierFeedPage() { +