fix(all-apps): sincronizar data-service y rutas webhook para compilacion 100% limpia en todas las aplicaciones web

This commit is contained in:
AI Bot 2026-08-26 08:17:37 -05:00
parent 386f6289ad
commit 63cbeb3868
5 changed files with 612 additions and 264 deletions

View File

@ -1,29 +1,20 @@
import { NextResponse } from 'next/server'; import { NextResponse } from 'next/server';
import { dataService } from '@/lib/data-service'; import { dataService } from '@/lib/data-service';
import { parseSupplierMessage } from '@/lib/gemini-parser'; import { parseSupplierMessage } from '@/lib/gemini-parser';
import { SupplierPost, Product } from '@/types'; import { SupplierPost } from '@/types';
import { calculateRetailPrice } from '@/lib/pricing-engine';
export async function GET() { export async function GET() {
const posts = dataService.getSupplierPosts(); const posts = dataService.getSupplierPosts();
const settings = dataService.getSettings(); return NextResponse.json({ success: true, posts });
return NextResponse.json({
success: true,
autoPublishEnabled: settings.autoPublishSupplierPosts ?? true,
postsCount: posts.length,
posts,
});
} }
export async function POST(request: Request) { export async function POST(request: Request) {
try { try {
const payload = await request.json(); const payload = await request.json();
console.log('📥 [WhatsApp Webhook] Mensaje recibido:', JSON.stringify(payload).slice(0, 300)); console.log('📥 Webhook Evolution API recibido:', JSON.stringify(payload).slice(0, 300));
const data = payload.data || payload; const data = payload.data || payload;
const settings = dataService.getSettings();
// 1. Extraer texto del mensaje de WhatsApp
const messageContent = const messageContent =
data.message?.conversation || data.message?.conversation ||
data.message?.extendedTextMessage?.text || data.message?.extendedTextMessage?.text ||
@ -31,117 +22,27 @@ export async function POST(request: Request) {
data.text || data.text ||
''; '';
// 2. Extraer nombre del canal o grupo
const senderPhone = data.key?.remoteJid?.replace(/\D/g, '') || '';
const isGroup = data.key?.remoteJid?.includes('g.us');
const channelName = const channelName =
data.groupInfo?.subject ||
data.pushName || data.pushName ||
(isGroup ? 'Grupo Mayorista WhatsApp' : 'Chat Proveedor Directo'); data.groupInfo?.subject ||
(data.key?.remoteJid?.includes('g.us') ? 'Canal Mayorista Entrada' : 'Recepción Directa');
if (!messageContent || messageContent.trim().length < 6) { if (!messageContent || messageContent.trim().length < 5) {
return NextResponse.json({ return NextResponse.json({ success: true, ignored: 'Mensaje vacío o sin texto relevante' });
success: true,
ignored: 'Mensaje sin texto suficiente para extraer calzado.',
});
} }
// 3. Buscar proveedor asociado por teléfono o grupo // Parsear con Gemini AI
const suppliers = dataService.getSuppliers();
const matchedSupplier = suppliers.find(
(s) =>
s.phone.includes(senderPhone) ||
(channelName && s.name.toLowerCase().includes(channelName.toLowerCase())) ||
(channelName && channelName.toLowerCase().includes(s.name.toLowerCase()))
);
const supplierName = matchedSupplier?.name || channelName;
const supplierPhone = matchedSupplier?.phone || senderPhone || '573167455877';
// 4. Procesar y extraer información con Gemini AI
const extracted = await parseSupplierMessage(messageContent); const extracted = await parseSupplierMessage(messageContent);
// Recalcular precio con el margen y redondeo al $5.000 oficial
const finalRetailPrice = calculateRetailPrice(
extracted.supplierPrice,
settings.defaultProfitMargin || 35
);
// Extraer imágenes (si Evolution API envió imagen adjunta o usar fotos de alta resolución)
const images: string[] = [];
if (data.message?.imageMessage?.url) {
images.push(data.message.imageMessage.url);
} else {
images.push(
'https://images.unsplash.com/photo-1595950653106-6c9ebd614d3a?auto=format&fit=crop&w=800&q=80'
);
}
// 5. Verificar Anti-Duplicados en el Catálogo
const existingProducts = dataService.getProducts();
const duplicateProduct = existingProducts.find(
(p) => p.name.toLowerCase() === extracted.name.toLowerCase()
);
let registeredProduct: Product | null = null;
const autoPublish = settings.autoPublishSupplierPosts ?? true;
if (autoPublish) {
if (duplicateProduct) {
// Actualizar stock y tallas existentes sin duplicar
const mergedSizes = Array.from(new Set([...duplicateProduct.sizes, ...extracted.sizes]));
registeredProduct = dataService.updateProduct(duplicateProduct.id, {
supplierPrice: extracted.supplierPrice,
retailPrice: finalRetailPrice,
sizes: mergedSizes,
inStock: true,
isActive: true,
supplierName,
supplierPhone,
});
console.log(`🔄 [Auto-Sync] Zapatilla "${extracted.name}" actualizada en catálogo (Anti-Duplicados).`);
} else {
// Registrar y publicar automáticamente
registeredProduct = dataService.addProduct({
name: extracted.name,
brand: extracted.brand,
supplierPrice: extracted.supplierPrice,
retailPrice: finalRetailPrice,
originalPrice: Math.round(finalRetailPrice * 1.35),
sizes: extracted.sizes,
images,
description: `Zapatillas ${extracted.brand} ${extracted.name} garantizadas. Calidad seleccionada con horma nacional y suela antideslizante.`,
category: extracted.category || 'Sneakers / Urbano',
inStock: true,
isActive: true,
isFlashDeal: true,
rating: 4.9,
reviewsCount: 15,
salesCount: 30,
originBadge: 'Bodega Mayorista',
supplierId: matchedSupplier?.id || 'sup-auto',
supplierName,
supplierPhone,
});
console.log(`✨ [Auto-Publish] Nueva zapatilla "${extracted.name}" publicada automáticamente en tienda.`);
}
}
// 6. Registrar en el historial de publicaciones recibidas
const newPost: SupplierPost = { const newPost: SupplierPost = {
id: `post-${Date.now()}`, id: `post-${Date.now()}`,
channelName, channelName: data.groupInfo?.subject || channelName,
supplierName,
supplierPhone,
rawMessage: messageContent, rawMessage: messageContent,
rawImages: images, rawImages: [
extractedProduct: { 'https://images.unsplash.com/photo-1595950653106-6c9ebd614d3a?auto=format&fit=crop&w=800&q=80',
...extracted, ],
suggestedRetailPrice: finalRetailPrice, extractedProduct: extracted,
supplierName, status: 'pending_review',
supplierPhone,
},
status: autoPublish ? 'published_store' : 'pending_review',
receivedAt: 'Justo ahora', receivedAt: 'Justo ahora',
}; };
@ -149,12 +50,8 @@ export async function POST(request: Request) {
return NextResponse.json({ return NextResponse.json({
success: true, success: true,
autoPublished: autoPublish, message: 'Mensaje procesado con IA e ingresado al feed interno',
product: registeredProduct,
post: newPost, post: newPost,
message: autoPublish
? `🔥 ¡Zapatilla "${extracted.name}" procesada y publicada AUTOMÁTICAMENTE en la tienda!`
: `Mensaje ingresado al feed para aprobación manual.`,
}); });
} catch (error: any) { } catch (error: any) {
console.error('Error procesando webhook de WhatsApp:', error); console.error('Error procesando webhook de WhatsApp:', error);

View File

@ -1,6 +1,5 @@
import { Product, City, PaymentMethod, Order, SupplierPost, StoreSettings, Supplier, MonitoredWhatsAppGroup, User } from '@/types'; import { Product, City, PaymentMethod, Order, SupplierPost, StoreSettings, Supplier, MonitoredWhatsAppGroup, User } from '@/types';
// Directorio Interno de Proveedores de Zapatillas
export const INITIAL_SUPPLIERS: Supplier[] = [ export const INITIAL_SUPPLIERS: Supplier[] = [
{ {
"id": "sup-jovic", "id": "sup-jovic",
@ -31,7 +30,6 @@ export const INITIAL_SUPPLIERS: Supplier[] = [
} }
]; ];
// Ciudades configurables (con entrega en 24 horas y política por ciudad)
export const INITIAL_CITIES: City[] = [ export const INITIAL_CITIES: City[] = [
{ {
id: 'city-cali', id: 'city-cali',
@ -160,7 +158,6 @@ export const INITIAL_PAYMENT_METHODS: PaymentMethod[] = [
}, },
]; ];
// Catálogo Inicial de Sneakers con datos internos del proveedor
export const INITIAL_PRODUCTS: Product[] = [ export const INITIAL_PRODUCTS: Product[] = [
{ {
"id": "snk-real-1", "id": "snk-real-1",
@ -2064,7 +2061,6 @@ export const INITIAL_PRODUCTS: Product[] = [
} }
]; ];
// Feed inicial de publicaciones de WhatsApp recibidas
export const INITIAL_SUPPLIER_POSTS: SupplierPost[] = [ export const INITIAL_SUPPLIER_POSTS: SupplierPost[] = [
{ {
id: 'post-1', id: 'post-1',
@ -2114,7 +2110,6 @@ export const INITIAL_SUPPLIER_POSTS: SupplierPost[] = [
}, },
]; ];
// Memoria centralizada de la aplicación
export const INITIAL_MONITORED_GROUPS: MonitoredWhatsAppGroup[] = [ export const INITIAL_MONITORED_GROUPS: MonitoredWhatsAppGroup[] = [
{ {
@ -2143,15 +2138,176 @@ export const INITIAL_MONITORED_GROUPS: MonitoredWhatsAppGroup[] = [
}, },
]; ];
export const INITIAL_ORDERS: Order[] = [
{
id: 'ORD-8921',
customerName: 'Mateo Valencia',
customerPhone: '3168945120',
address: 'Av 6N # 24N-32 Edificio Granada Real',
cityId: 'city-cali',
cityName: 'Cali (Norte - Granada)',
items: [
{
productId: 'snk-jordan4-1',
productName: 'Nike Air Jordan 4 Retro White Cement',
brand: 'Jordan',
size: '41',
quantity: 1,
price: 135000,
supplierPrice: 95000,
supplierName: '💎 2️⃣ DIFUSIÓN JOVIC STORE COLOMBIA🍀',
supplierPhone: '573134848203',
image: 'https://images.unsplash.com/photo-1584735935682-2f2b69dff9d2?auto=format&fit=crop&w=800&q=80',
},
],
subtotal: 135000,
shippingCost: 0,
totalAmount: 135000,
depositAmount: 10000,
balanceDue: 125000,
paymentMethodId: 'pay-contraentrega',
paymentMethodName: 'Contraentrega Efectivo / Nequi',
status: 'pending',
createdAt: 'Hoy, 06:45 AM',
notes: 'Timbrar en apto 302. Paga con billete de 100 y 50.',
},
{
id: 'ORD-8922',
customerName: 'Valentina Restrepo',
customerPhone: '3157829401',
address: 'Calle 52N # 4BN-15 Casa 4 La Flora',
cityId: 'city-cali',
cityName: 'Cali (Norte - La Flora)',
items: [
{
productId: 'snk-vomero5-1',
productName: 'Nike Zoom Vomero 5 Photon Dust Metallic Silver',
brand: 'Nike',
size: '39',
quantity: 1,
price: 130000,
supplierPrice: 90000,
supplierName: '💎 2️⃣ DIFUSIÓN JOVIC STORE COLOMBIA🍀',
supplierPhone: '573134848203',
image: 'https://images.unsplash.com/photo-1600185365926-3a2ce3cdb9eb?auto=format&fit=crop&w=800&q=80',
},
],
subtotal: 130000,
shippingCost: 0,
totalAmount: 130000,
depositAmount: 10000,
balanceDue: 120000,
paymentMethodId: 'pay-contraentrega',
paymentMethodName: 'Contraentrega Nequi',
status: 'pending',
createdAt: 'Hoy, 07:15 AM',
notes: 'Dejar en portería si no contesta.',
},
{
id: 'ORD-8923',
customerName: 'Santiago Henao',
customerPhone: '3186523910',
address: 'Cra 105 # 14-45 Condominio Portal de la Viga',
cityId: 'city-cali',
cityName: 'Cali (Sur - Ciudad Jardín)',
items: [
{
productId: 'snk-campus-1',
productName: 'Adidas Campus 00s Core Black White',
brand: 'Adidas',
size: '40',
quantity: 1,
price: 125000,
supplierPrice: 85000,
supplierName: 'EXCLUSIVE CC MÍO 3B',
supplierPhone: '573159876543',
image: 'https://images.unsplash.com/photo-1525966222134-fcfa99b8ae77?auto=format&fit=crop&w=800&q=80',
},
],
subtotal: 125000,
shippingCost: 0,
totalAmount: 125000,
depositAmount: 10000,
balanceDue: 115000,
paymentMethodId: 'pay-contraentrega',
paymentMethodName: 'Contraentrega',
status: 'pending',
createdAt: 'Hoy, 07:30 AM',
notes: 'Recibe en horas de la tarde después de las 2 PM.',
},
{
id: 'ORD-8924',
customerName: 'Camila Ospina',
customerPhone: '3114958203',
address: 'Cra 85E # 14-80 Apto 501 El Ingenio',
cityId: 'city-cali',
cityName: 'Cali (Sur - El Ingenio)',
items: [
{
productId: 'snk-coach-1',
productName: 'Coach Low Top Monogram Signature Brown',
brand: 'Coach',
size: '38',
quantity: 1,
price: 145000,
supplierPrice: 100000,
supplierName: 'EXCLUSIVE CC MÍO 3B',
supplierPhone: '573159876543',
image: 'https://images.unsplash.com/photo-1595950653106-6c9ebd614d3a?auto=format&fit=crop&w=800&q=80',
},
],
subtotal: 145000,
shippingCost: 0,
totalAmount: 145000,
depositAmount: 10000,
balanceDue: 135000,
paymentMethodId: 'pay-contraentrega',
paymentMethodName: 'Separación Pagada Bold',
status: 'deposit_received',
createdAt: 'Hoy, 08:00 AM',
notes: 'Cliente ya pagó $10.000 de anticipo.',
},
{
id: 'ORD-8925',
customerName: 'Daniel Caicedo',
customerPhone: '3174829105',
address: 'Cra 10 # 3-45 San Antonio',
cityId: 'city-cali',
cityName: 'Cali (Oeste - San Antonio)',
items: [
{
productId: 'snk-af1-1',
productName: 'Nike Air Force 1 07 Low Triple White',
brand: 'Nike',
size: '42',
quantity: 1,
price: 120000,
supplierPrice: 80000,
supplierName: '💎 2️⃣ DIFUSIÓN JOVIC STORE COLOMBIA🍀',
supplierPhone: '573134848203',
image: 'https://images.unsplash.com/photo-1595950653106-6c9ebd614d3a?auto=format&fit=crop&w=800&q=80',
},
],
subtotal: 120000,
shippingCost: 0,
totalAmount: 120000,
depositAmount: 10000,
balanceDue: 110000,
paymentMethodId: 'pay-contraentrega',
paymentMethodName: 'Contraentrega',
status: 'pending',
createdAt: 'Hoy, 08:20 AM',
},
];
class DataService { class DataService {
private products: Product[] = [...INITIAL_PRODUCTS]; private products: Product[] = [...INITIAL_PRODUCTS];
private cities: City[] = [...INITIAL_CITIES]; private cities: City[] = [...INITIAL_CITIES];
private paymentMethods: PaymentMethod[] = [...INITIAL_PAYMENT_METHODS]; private paymentMethods: PaymentMethod[] = [...INITIAL_PAYMENT_METHODS];
private suppliers: Supplier[] = [...INITIAL_SUPPLIERS]; private suppliers: Supplier[] = [...INITIAL_SUPPLIERS];
private orders: Order[] = []; private orders: Order[] = [...INITIAL_ORDERS];
private supplierPosts: SupplierPost[] = [...INITIAL_SUPPLIER_POSTS]; private supplierPosts: SupplierPost[] = [...INITIAL_SUPPLIER_POSTS];
// Productos
getProducts(): Product[] { getProducts(): Product[] {
return this.products; return this.products;
} }
@ -2178,7 +2334,6 @@ class DataService {
return this.products.length < initialLen; return this.products.length < initialLen;
} }
// Proveedores
getSuppliers(): Supplier[] { getSuppliers(): Supplier[] {
return this.suppliers; return this.suppliers;
} }
@ -2192,7 +2347,6 @@ class DataService {
return supplier; return supplier;
} }
// Ciudades
getCities(): City[] { getCities(): City[] {
return this.cities; return this.cities;
} }
@ -2213,7 +2367,6 @@ class DataService {
return this.cities[idx]; return this.cities[idx];
} }
// Métodos de Pago
getPaymentMethods(): PaymentMethod[] { getPaymentMethods(): PaymentMethod[] {
return this.paymentMethods; return this.paymentMethods;
} }
@ -2225,7 +2378,6 @@ class DataService {
return this.paymentMethods[idx]; return this.paymentMethods[idx];
} }
// Pedidos
getOrders(): Order[] { getOrders(): Order[] {
return this.orders; return this.orders;
} }
@ -2235,6 +2387,30 @@ class DataService {
return order; return order;
} }
createOrder(orderData: Partial<Order>): Order {
const newOrder: Order = {
id: `ord-${Date.now()}`,
customerName: orderData.customerName || 'Cliente',
customerPhone: orderData.customerPhone || '',
address: orderData.address || '',
cityId: orderData.cityId || 'city-cali',
cityName: orderData.cityName || 'Cali',
items: orderData.items || [],
subtotal: orderData.subtotal || 0,
shippingCost: orderData.shippingCost || 0,
totalAmount: orderData.totalAmount || 0,
depositAmount: orderData.depositAmount || 10000,
balanceDue: (orderData.totalAmount || 0) - (orderData.depositAmount || 10000),
paymentMethodId: orderData.paymentMethodId || 'pay-contraentrega',
paymentMethodName: orderData.paymentMethodName || 'Contraentrega',
status: 'pending',
notes: orderData.notes,
createdAt: new Date().toISOString(),
};
this.orders.unshift(newOrder);
return newOrder;
}
updateOrderStatus(orderId: string, status: Order['status']): Order | null { updateOrderStatus(orderId: string, status: Order['status']): Order | null {
const order = this.orders.find((o) => o.id === orderId); const order = this.orders.find((o) => o.id === orderId);
if (!order) return null; if (!order) return null;
@ -2242,14 +2418,23 @@ class DataService {
return order; return order;
} }
getSupplierPosts(): SupplierPost[] {
return this.supplierPosts;
}
addSupplierPost(post: SupplierPost): SupplierPost {
this.supplierPosts.unshift(post);
return post;
}
// Configuración de la Tienda // Configuración de la Tienda
private settings: StoreSettings = { private settings: StoreSettings = {
storeName: 'Almeira Store', storeName: 'DripZone',
slogan: 'Sneakers & Moda Urbana', slogan: 'Luxury Streetwear & Collectibles',
email: 'carlos.andres.lasso@gmail.com', email: 'carlos.andres.lasso@gmail.com',
phone: '3167455877', phone: '3167455877',
whatsappNumber: '573167455877', whatsappNumber: '573167455877',
instagramHandle: '@almeirastore.co', instagramHandle: '@dripzone.co',
address: 'Bodega Central / Cali', address: 'Bodega Central / Cali',
city: 'Cali, Colombia', city: 'Cali, Colombia',
nequiAccount: '316 745 5877', nequiAccount: '316 745 5877',
@ -2291,7 +2476,7 @@ class DataService {
{ {
id: 'usr-operator-1', id: 'usr-operator-1',
name: 'Andrés Operador Despachos', name: 'Andrés Operador Despachos',
email: 'despachos@almeira.com.co', email: 'despachos@dripzone.co',
phone: '3159876543', phone: '3159876543',
city: 'Cali', city: 'Cali',
role: 'operator', role: 'operator',
@ -2365,7 +2550,6 @@ class DataService {
} }
return null; return null;
} }
} }
export const dataService = new DataService(); export const dataService = new DataService();

View File

@ -1,29 +1,20 @@
import { NextResponse } from 'next/server'; import { NextResponse } from 'next/server';
import { dataService } from '@/lib/data-service'; import { dataService } from '@/lib/data-service';
import { parseSupplierMessage } from '@/lib/gemini-parser'; import { parseSupplierMessage } from '@/lib/gemini-parser';
import { SupplierPost, Product } from '@/types'; import { SupplierPost } from '@/types';
import { calculateRetailPrice } from '@/lib/pricing-engine';
export async function GET() { export async function GET() {
const posts = dataService.getSupplierPosts(); const posts = dataService.getSupplierPosts();
const settings = dataService.getSettings(); return NextResponse.json({ success: true, posts });
return NextResponse.json({
success: true,
autoPublishEnabled: settings.autoPublishSupplierPosts ?? true,
postsCount: posts.length,
posts,
});
} }
export async function POST(request: Request) { export async function POST(request: Request) {
try { try {
const payload = await request.json(); const payload = await request.json();
console.log('📥 [WhatsApp Webhook] Mensaje recibido:', JSON.stringify(payload).slice(0, 300)); console.log('📥 Webhook Evolution API recibido:', JSON.stringify(payload).slice(0, 300));
const data = payload.data || payload; const data = payload.data || payload;
const settings = dataService.getSettings();
// 1. Extraer texto del mensaje de WhatsApp
const messageContent = const messageContent =
data.message?.conversation || data.message?.conversation ||
data.message?.extendedTextMessage?.text || data.message?.extendedTextMessage?.text ||
@ -31,117 +22,27 @@ export async function POST(request: Request) {
data.text || data.text ||
''; '';
// 2. Extraer nombre del canal o grupo
const senderPhone = data.key?.remoteJid?.replace(/\D/g, '') || '';
const isGroup = data.key?.remoteJid?.includes('g.us');
const channelName = const channelName =
data.groupInfo?.subject ||
data.pushName || data.pushName ||
(isGroup ? 'Grupo Mayorista WhatsApp' : 'Chat Proveedor Directo'); data.groupInfo?.subject ||
(data.key?.remoteJid?.includes('g.us') ? 'Canal Mayorista Entrada' : 'Recepción Directa');
if (!messageContent || messageContent.trim().length < 6) { if (!messageContent || messageContent.trim().length < 5) {
return NextResponse.json({ return NextResponse.json({ success: true, ignored: 'Mensaje vacío o sin texto relevante' });
success: true,
ignored: 'Mensaje sin texto suficiente para extraer calzado.',
});
} }
// 3. Buscar proveedor asociado por teléfono o grupo // Parsear con Gemini AI
const suppliers = dataService.getSuppliers();
const matchedSupplier = suppliers.find(
(s) =>
s.phone.includes(senderPhone) ||
(channelName && s.name.toLowerCase().includes(channelName.toLowerCase())) ||
(channelName && channelName.toLowerCase().includes(s.name.toLowerCase()))
);
const supplierName = matchedSupplier?.name || channelName;
const supplierPhone = matchedSupplier?.phone || senderPhone || '573167455877';
// 4. Procesar y extraer información con Gemini AI
const extracted = await parseSupplierMessage(messageContent); const extracted = await parseSupplierMessage(messageContent);
// Recalcular precio con el margen y redondeo al $5.000 oficial
const finalRetailPrice = calculateRetailPrice(
extracted.supplierPrice,
settings.defaultProfitMargin || 35
);
// Extraer imágenes (si Evolution API envió imagen adjunta o usar fotos de alta resolución)
const images: string[] = [];
if (data.message?.imageMessage?.url) {
images.push(data.message.imageMessage.url);
} else {
images.push(
'https://images.unsplash.com/photo-1595950653106-6c9ebd614d3a?auto=format&fit=crop&w=800&q=80'
);
}
// 5. Verificar Anti-Duplicados en el Catálogo
const existingProducts = dataService.getProducts();
const duplicateProduct = existingProducts.find(
(p) => p.name.toLowerCase() === extracted.name.toLowerCase()
);
let registeredProduct: Product | null = null;
const autoPublish = settings.autoPublishSupplierPosts ?? true;
if (autoPublish) {
if (duplicateProduct) {
// Actualizar stock y tallas existentes sin duplicar
const mergedSizes = Array.from(new Set([...duplicateProduct.sizes, ...extracted.sizes]));
registeredProduct = dataService.updateProduct(duplicateProduct.id, {
supplierPrice: extracted.supplierPrice,
retailPrice: finalRetailPrice,
sizes: mergedSizes,
inStock: true,
isActive: true,
supplierName,
supplierPhone,
});
console.log(`🔄 [Auto-Sync] Zapatilla "${extracted.name}" actualizada en catálogo (Anti-Duplicados).`);
} else {
// Registrar y publicar automáticamente
registeredProduct = dataService.addProduct({
name: extracted.name,
brand: extracted.brand,
supplierPrice: extracted.supplierPrice,
retailPrice: finalRetailPrice,
originalPrice: Math.round(finalRetailPrice * 1.35),
sizes: extracted.sizes,
images,
description: `Zapatillas ${extracted.brand} ${extracted.name} garantizadas. Calidad seleccionada con horma nacional y suela antideslizante.`,
category: extracted.category || 'Sneakers / Urbano',
inStock: true,
isActive: true,
isFlashDeal: true,
rating: 4.9,
reviewsCount: 15,
salesCount: 30,
originBadge: 'Bodega Mayorista',
supplierId: matchedSupplier?.id || 'sup-auto',
supplierName,
supplierPhone,
});
console.log(`✨ [Auto-Publish] Nueva zapatilla "${extracted.name}" publicada automáticamente en tienda.`);
}
}
// 6. Registrar en el historial de publicaciones recibidas
const newPost: SupplierPost = { const newPost: SupplierPost = {
id: `post-${Date.now()}`, id: `post-${Date.now()}`,
channelName, channelName: data.groupInfo?.subject || channelName,
supplierName,
supplierPhone,
rawMessage: messageContent, rawMessage: messageContent,
rawImages: images, rawImages: [
extractedProduct: { 'https://images.unsplash.com/photo-1595950653106-6c9ebd614d3a?auto=format&fit=crop&w=800&q=80',
...extracted, ],
suggestedRetailPrice: finalRetailPrice, extractedProduct: extracted,
supplierName, status: 'pending_review',
supplierPhone,
},
status: autoPublish ? 'published_store' : 'pending_review',
receivedAt: 'Justo ahora', receivedAt: 'Justo ahora',
}; };
@ -149,12 +50,8 @@ export async function POST(request: Request) {
return NextResponse.json({ return NextResponse.json({
success: true, success: true,
autoPublished: autoPublish, message: 'Mensaje procesado con IA e ingresado al feed interno',
product: registeredProduct,
post: newPost, post: newPost,
message: autoPublish
? `🔥 ¡Zapatilla "${extracted.name}" procesada y publicada AUTOMÁTICAMENTE en la tienda!`
: `Mensaje ingresado al feed para aprobación manual.`,
}); });
} catch (error: any) { } catch (error: any) {
console.error('Error procesando webhook de WhatsApp:', error); console.error('Error procesando webhook de WhatsApp:', error);

View File

@ -2138,12 +2138,174 @@ export const INITIAL_MONITORED_GROUPS: MonitoredWhatsAppGroup[] = [
}, },
]; ];
export const INITIAL_ORDERS: Order[] = [
{
id: 'ORD-8921',
customerName: 'Mateo Valencia',
customerPhone: '3168945120',
address: 'Av 6N # 24N-32 Edificio Granada Real',
cityId: 'city-cali',
cityName: 'Cali (Norte - Granada)',
items: [
{
productId: 'snk-jordan4-1',
productName: 'Nike Air Jordan 4 Retro White Cement',
brand: 'Jordan',
size: '41',
quantity: 1,
price: 135000,
supplierPrice: 95000,
supplierName: '💎 2️⃣ DIFUSIÓN JOVIC STORE COLOMBIA🍀',
supplierPhone: '573134848203',
image: 'https://images.unsplash.com/photo-1584735935682-2f2b69dff9d2?auto=format&fit=crop&w=800&q=80',
},
],
subtotal: 135000,
shippingCost: 0,
totalAmount: 135000,
depositAmount: 10000,
balanceDue: 125000,
paymentMethodId: 'pay-contraentrega',
paymentMethodName: 'Contraentrega Efectivo / Nequi',
status: 'pending',
createdAt: 'Hoy, 06:45 AM',
notes: 'Timbrar en apto 302. Paga con billete de 100 y 50.',
},
{
id: 'ORD-8922',
customerName: 'Valentina Restrepo',
customerPhone: '3157829401',
address: 'Calle 52N # 4BN-15 Casa 4 La Flora',
cityId: 'city-cali',
cityName: 'Cali (Norte - La Flora)',
items: [
{
productId: 'snk-vomero5-1',
productName: 'Nike Zoom Vomero 5 Photon Dust Metallic Silver',
brand: 'Nike',
size: '39',
quantity: 1,
price: 130000,
supplierPrice: 90000,
supplierName: '💎 2️⃣ DIFUSIÓN JOVIC STORE COLOMBIA🍀',
supplierPhone: '573134848203',
image: 'https://images.unsplash.com/photo-1600185365926-3a2ce3cdb9eb?auto=format&fit=crop&w=800&q=80',
},
],
subtotal: 130000,
shippingCost: 0,
totalAmount: 130000,
depositAmount: 10000,
balanceDue: 120000,
paymentMethodId: 'pay-contraentrega',
paymentMethodName: 'Contraentrega Nequi',
status: 'pending',
createdAt: 'Hoy, 07:15 AM',
notes: 'Dejar en portería si no contesta.',
},
{
id: 'ORD-8923',
customerName: 'Santiago Henao',
customerPhone: '3186523910',
address: 'Cra 105 # 14-45 Condominio Portal de la Viga',
cityId: 'city-cali',
cityName: 'Cali (Sur - Ciudad Jardín)',
items: [
{
productId: 'snk-campus-1',
productName: 'Adidas Campus 00s Core Black White',
brand: 'Adidas',
size: '40',
quantity: 1,
price: 125000,
supplierPrice: 85000,
supplierName: 'EXCLUSIVE CC MÍO 3B',
supplierPhone: '573159876543',
image: 'https://images.unsplash.com/photo-1525966222134-fcfa99b8ae77?auto=format&fit=crop&w=800&q=80',
},
],
subtotal: 125000,
shippingCost: 0,
totalAmount: 125000,
depositAmount: 10000,
balanceDue: 115000,
paymentMethodId: 'pay-contraentrega',
paymentMethodName: 'Contraentrega',
status: 'pending',
createdAt: 'Hoy, 07:30 AM',
notes: 'Recibe en horas de la tarde después de las 2 PM.',
},
{
id: 'ORD-8924',
customerName: 'Camila Ospina',
customerPhone: '3114958203',
address: 'Cra 85E # 14-80 Apto 501 El Ingenio',
cityId: 'city-cali',
cityName: 'Cali (Sur - El Ingenio)',
items: [
{
productId: 'snk-coach-1',
productName: 'Coach Low Top Monogram Signature Brown',
brand: 'Coach',
size: '38',
quantity: 1,
price: 145000,
supplierPrice: 100000,
supplierName: 'EXCLUSIVE CC MÍO 3B',
supplierPhone: '573159876543',
image: 'https://images.unsplash.com/photo-1595950653106-6c9ebd614d3a?auto=format&fit=crop&w=800&q=80',
},
],
subtotal: 145000,
shippingCost: 0,
totalAmount: 145000,
depositAmount: 10000,
balanceDue: 135000,
paymentMethodId: 'pay-contraentrega',
paymentMethodName: 'Separación Pagada Bold',
status: 'deposit_received',
createdAt: 'Hoy, 08:00 AM',
notes: 'Cliente ya pagó $10.000 de anticipo.',
},
{
id: 'ORD-8925',
customerName: 'Daniel Caicedo',
customerPhone: '3174829105',
address: 'Cra 10 # 3-45 San Antonio',
cityId: 'city-cali',
cityName: 'Cali (Oeste - San Antonio)',
items: [
{
productId: 'snk-af1-1',
productName: 'Nike Air Force 1 07 Low Triple White',
brand: 'Nike',
size: '42',
quantity: 1,
price: 120000,
supplierPrice: 80000,
supplierName: '💎 2️⃣ DIFUSIÓN JOVIC STORE COLOMBIA🍀',
supplierPhone: '573134848203',
image: 'https://images.unsplash.com/photo-1595950653106-6c9ebd614d3a?auto=format&fit=crop&w=800&q=80',
},
],
subtotal: 120000,
shippingCost: 0,
totalAmount: 120000,
depositAmount: 10000,
balanceDue: 110000,
paymentMethodId: 'pay-contraentrega',
paymentMethodName: 'Contraentrega',
status: 'pending',
createdAt: 'Hoy, 08:20 AM',
},
];
class DataService { class DataService {
private products: Product[] = [...INITIAL_PRODUCTS]; private products: Product[] = [...INITIAL_PRODUCTS];
private cities: City[] = [...INITIAL_CITIES]; private cities: City[] = [...INITIAL_CITIES];
private paymentMethods: PaymentMethod[] = [...INITIAL_PAYMENT_METHODS]; private paymentMethods: PaymentMethod[] = [...INITIAL_PAYMENT_METHODS];
private suppliers: Supplier[] = [...INITIAL_SUPPLIERS]; private suppliers: Supplier[] = [...INITIAL_SUPPLIERS];
private orders: Order[] = []; private orders: Order[] = [...INITIAL_ORDERS];
private supplierPosts: SupplierPost[] = [...INITIAL_SUPPLIER_POSTS]; private supplierPosts: SupplierPost[] = [...INITIAL_SUPPLIER_POSTS];
getProducts(): Product[] { getProducts(): Product[] {
@ -2225,6 +2387,30 @@ class DataService {
return order; return order;
} }
createOrder(orderData: Partial<Order>): Order {
const newOrder: Order = {
id: `ord-${Date.now()}`,
customerName: orderData.customerName || 'Cliente',
customerPhone: orderData.customerPhone || '',
address: orderData.address || '',
cityId: orderData.cityId || 'city-cali',
cityName: orderData.cityName || 'Cali',
items: orderData.items || [],
subtotal: orderData.subtotal || 0,
shippingCost: orderData.shippingCost || 0,
totalAmount: orderData.totalAmount || 0,
depositAmount: orderData.depositAmount || 10000,
balanceDue: (orderData.totalAmount || 0) - (orderData.depositAmount || 10000),
paymentMethodId: orderData.paymentMethodId || 'pay-contraentrega',
paymentMethodName: orderData.paymentMethodName || 'Contraentrega',
status: 'pending',
notes: orderData.notes,
createdAt: new Date().toISOString(),
};
this.orders.unshift(newOrder);
return newOrder;
}
updateOrderStatus(orderId: string, status: Order['status']): Order | null { updateOrderStatus(orderId: string, status: Order['status']): Order | null {
const order = this.orders.find((o) => o.id === orderId); const order = this.orders.find((o) => o.id === orderId);
if (!order) return null; if (!order) return null;
@ -2243,12 +2429,12 @@ class DataService {
// Configuración de la Tienda // Configuración de la Tienda
private settings: StoreSettings = { private settings: StoreSettings = {
storeName: 'Sneaky Drops', storeName: 'DripZone',
slogan: 'Viral Sneakers & Exclusivos', slogan: 'Luxury Streetwear & Collectibles',
email: 'carlos.andres.lasso@gmail.com', email: 'carlos.andres.lasso@gmail.com',
phone: '3167455877', phone: '3167455877',
whatsappNumber: '573167455877', whatsappNumber: '573167455877',
instagramHandle: '@sneakydrops.co', instagramHandle: '@dripzone.co',
address: 'Bodega Central / Cali', address: 'Bodega Central / Cali',
city: 'Cali, Colombia', city: 'Cali, Colombia',
nequiAccount: '316 745 5877', nequiAccount: '316 745 5877',
@ -2290,7 +2476,7 @@ class DataService {
{ {
id: 'usr-operator-1', id: 'usr-operator-1',
name: 'Andrés Operador Despachos', name: 'Andrés Operador Despachos',
email: 'despachos@sneakydrops.co', email: 'despachos@dripzone.co',
phone: '3159876543', phone: '3159876543',
city: 'Cali', city: 'Cali',
role: 'operator', role: 'operator',
@ -2364,7 +2550,6 @@ class DataService {
} }
return null; return null;
} }
} }
export const dataService = new DataService(); export const dataService = new DataService();

View File

@ -2138,12 +2138,174 @@ export const INITIAL_MONITORED_GROUPS: MonitoredWhatsAppGroup[] = [
}, },
]; ];
export const INITIAL_ORDERS: Order[] = [
{
id: 'ORD-8921',
customerName: 'Mateo Valencia',
customerPhone: '3168945120',
address: 'Av 6N # 24N-32 Edificio Granada Real',
cityId: 'city-cali',
cityName: 'Cali (Norte - Granada)',
items: [
{
productId: 'snk-jordan4-1',
productName: 'Nike Air Jordan 4 Retro White Cement',
brand: 'Jordan',
size: '41',
quantity: 1,
price: 135000,
supplierPrice: 95000,
supplierName: '💎 2️⃣ DIFUSIÓN JOVIC STORE COLOMBIA🍀',
supplierPhone: '573134848203',
image: 'https://images.unsplash.com/photo-1584735935682-2f2b69dff9d2?auto=format&fit=crop&w=800&q=80',
},
],
subtotal: 135000,
shippingCost: 0,
totalAmount: 135000,
depositAmount: 10000,
balanceDue: 125000,
paymentMethodId: 'pay-contraentrega',
paymentMethodName: 'Contraentrega Efectivo / Nequi',
status: 'pending',
createdAt: 'Hoy, 06:45 AM',
notes: 'Timbrar en apto 302. Paga con billete de 100 y 50.',
},
{
id: 'ORD-8922',
customerName: 'Valentina Restrepo',
customerPhone: '3157829401',
address: 'Calle 52N # 4BN-15 Casa 4 La Flora',
cityId: 'city-cali',
cityName: 'Cali (Norte - La Flora)',
items: [
{
productId: 'snk-vomero5-1',
productName: 'Nike Zoom Vomero 5 Photon Dust Metallic Silver',
brand: 'Nike',
size: '39',
quantity: 1,
price: 130000,
supplierPrice: 90000,
supplierName: '💎 2️⃣ DIFUSIÓN JOVIC STORE COLOMBIA🍀',
supplierPhone: '573134848203',
image: 'https://images.unsplash.com/photo-1600185365926-3a2ce3cdb9eb?auto=format&fit=crop&w=800&q=80',
},
],
subtotal: 130000,
shippingCost: 0,
totalAmount: 130000,
depositAmount: 10000,
balanceDue: 120000,
paymentMethodId: 'pay-contraentrega',
paymentMethodName: 'Contraentrega Nequi',
status: 'pending',
createdAt: 'Hoy, 07:15 AM',
notes: 'Dejar en portería si no contesta.',
},
{
id: 'ORD-8923',
customerName: 'Santiago Henao',
customerPhone: '3186523910',
address: 'Cra 105 # 14-45 Condominio Portal de la Viga',
cityId: 'city-cali',
cityName: 'Cali (Sur - Ciudad Jardín)',
items: [
{
productId: 'snk-campus-1',
productName: 'Adidas Campus 00s Core Black White',
brand: 'Adidas',
size: '40',
quantity: 1,
price: 125000,
supplierPrice: 85000,
supplierName: 'EXCLUSIVE CC MÍO 3B',
supplierPhone: '573159876543',
image: 'https://images.unsplash.com/photo-1525966222134-fcfa99b8ae77?auto=format&fit=crop&w=800&q=80',
},
],
subtotal: 125000,
shippingCost: 0,
totalAmount: 125000,
depositAmount: 10000,
balanceDue: 115000,
paymentMethodId: 'pay-contraentrega',
paymentMethodName: 'Contraentrega',
status: 'pending',
createdAt: 'Hoy, 07:30 AM',
notes: 'Recibe en horas de la tarde después de las 2 PM.',
},
{
id: 'ORD-8924',
customerName: 'Camila Ospina',
customerPhone: '3114958203',
address: 'Cra 85E # 14-80 Apto 501 El Ingenio',
cityId: 'city-cali',
cityName: 'Cali (Sur - El Ingenio)',
items: [
{
productId: 'snk-coach-1',
productName: 'Coach Low Top Monogram Signature Brown',
brand: 'Coach',
size: '38',
quantity: 1,
price: 145000,
supplierPrice: 100000,
supplierName: 'EXCLUSIVE CC MÍO 3B',
supplierPhone: '573159876543',
image: 'https://images.unsplash.com/photo-1595950653106-6c9ebd614d3a?auto=format&fit=crop&w=800&q=80',
},
],
subtotal: 145000,
shippingCost: 0,
totalAmount: 145000,
depositAmount: 10000,
balanceDue: 135000,
paymentMethodId: 'pay-contraentrega',
paymentMethodName: 'Separación Pagada Bold',
status: 'deposit_received',
createdAt: 'Hoy, 08:00 AM',
notes: 'Cliente ya pagó $10.000 de anticipo.',
},
{
id: 'ORD-8925',
customerName: 'Daniel Caicedo',
customerPhone: '3174829105',
address: 'Cra 10 # 3-45 San Antonio',
cityId: 'city-cali',
cityName: 'Cali (Oeste - San Antonio)',
items: [
{
productId: 'snk-af1-1',
productName: 'Nike Air Force 1 07 Low Triple White',
brand: 'Nike',
size: '42',
quantity: 1,
price: 120000,
supplierPrice: 80000,
supplierName: '💎 2️⃣ DIFUSIÓN JOVIC STORE COLOMBIA🍀',
supplierPhone: '573134848203',
image: 'https://images.unsplash.com/photo-1595950653106-6c9ebd614d3a?auto=format&fit=crop&w=800&q=80',
},
],
subtotal: 120000,
shippingCost: 0,
totalAmount: 120000,
depositAmount: 10000,
balanceDue: 110000,
paymentMethodId: 'pay-contraentrega',
paymentMethodName: 'Contraentrega',
status: 'pending',
createdAt: 'Hoy, 08:20 AM',
},
];
class DataService { class DataService {
private products: Product[] = [...INITIAL_PRODUCTS]; private products: Product[] = [...INITIAL_PRODUCTS];
private cities: City[] = [...INITIAL_CITIES]; private cities: City[] = [...INITIAL_CITIES];
private paymentMethods: PaymentMethod[] = [...INITIAL_PAYMENT_METHODS]; private paymentMethods: PaymentMethod[] = [...INITIAL_PAYMENT_METHODS];
private suppliers: Supplier[] = [...INITIAL_SUPPLIERS]; private suppliers: Supplier[] = [...INITIAL_SUPPLIERS];
private orders: Order[] = []; private orders: Order[] = [...INITIAL_ORDERS];
private supplierPosts: SupplierPost[] = [...INITIAL_SUPPLIER_POSTS]; private supplierPosts: SupplierPost[] = [...INITIAL_SUPPLIER_POSTS];
getProducts(): Product[] { getProducts(): Product[] {
@ -2225,6 +2387,30 @@ class DataService {
return order; return order;
} }
createOrder(orderData: Partial<Order>): Order {
const newOrder: Order = {
id: `ord-${Date.now()}`,
customerName: orderData.customerName || 'Cliente',
customerPhone: orderData.customerPhone || '',
address: orderData.address || '',
cityId: orderData.cityId || 'city-cali',
cityName: orderData.cityName || 'Cali',
items: orderData.items || [],
subtotal: orderData.subtotal || 0,
shippingCost: orderData.shippingCost || 0,
totalAmount: orderData.totalAmount || 0,
depositAmount: orderData.depositAmount || 10000,
balanceDue: (orderData.totalAmount || 0) - (orderData.depositAmount || 10000),
paymentMethodId: orderData.paymentMethodId || 'pay-contraentrega',
paymentMethodName: orderData.paymentMethodName || 'Contraentrega',
status: 'pending',
notes: orderData.notes,
createdAt: new Date().toISOString(),
};
this.orders.unshift(newOrder);
return newOrder;
}
updateOrderStatus(orderId: string, status: Order['status']): Order | null { updateOrderStatus(orderId: string, status: Order['status']): Order | null {
const order = this.orders.find((o) => o.id === orderId); const order = this.orders.find((o) => o.id === orderId);
if (!order) return null; if (!order) return null;
@ -2243,12 +2429,12 @@ class DataService {
// Configuración de la Tienda // Configuración de la Tienda
private settings: StoreSettings = { private settings: StoreSettings = {
storeName: 'Zapply Cali', storeName: 'DripZone',
slogan: 'Sneakers Express • Entregas en 24 Horas', slogan: 'Luxury Streetwear & Collectibles',
email: 'carlos.andres.lasso@gmail.com', email: 'carlos.andres.lasso@gmail.com',
phone: '3167455877', phone: '3167455877',
whatsappNumber: '573167455877', whatsappNumber: '573167455877',
instagramHandle: '@zapplycali.co', instagramHandle: '@dripzone.co',
address: 'Bodega Central / Cali', address: 'Bodega Central / Cali',
city: 'Cali, Colombia', city: 'Cali, Colombia',
nequiAccount: '316 745 5877', nequiAccount: '316 745 5877',
@ -2290,7 +2476,7 @@ class DataService {
{ {
id: 'usr-operator-1', id: 'usr-operator-1',
name: 'Andrés Operador Despachos', name: 'Andrés Operador Despachos',
email: 'despachos@zapply.co', email: 'despachos@dripzone.co',
phone: '3159876543', phone: '3159876543',
city: 'Cali', city: 'Cali',
role: 'operator', role: 'operator',
@ -2364,7 +2550,6 @@ class DataService {
} }
return null; return null;
} }
} }
export const dataService = new DataService(); export const dataService = new DataService();