diff --git a/dripzone-app/src/lib/data-service.ts b/dripzone-app/src/lib/data-service.ts
index 56f386a..d2e4c78 100644
--- a/dripzone-app/src/lib/data-service.ts
+++ b/dripzone-app/src/lib/data-service.ts
@@ -188,8 +188,70 @@ export const INITIAL_USERS: User[] = [
export const INITIAL_MONITORED_GROUPS: MonitoredWhatsAppGroup[] = [];
+function getFsModule() {
+ if (typeof window === 'undefined') {
+ try {
+ return require('fs');
+ } catch (_) {
+ return null;
+ }
+ }
+ return null;
+}
+
+function getPathModule() {
+ if (typeof window === 'undefined') {
+ try {
+ return require('path');
+ } catch (_) {
+ return null;
+ }
+ }
+ return null;
+}
+
+function getPersistedFilePath(): string | null {
+ const pathMod = getPathModule();
+ if (!pathMod) return null;
+ return pathMod.join(process.cwd(), 'data', 'persisted_products.json');
+}
+
+function loadPersistedProducts(): Product[] {
+ const fsMod = getFsModule();
+ const filePath = getPersistedFilePath();
+ if (fsMod && filePath && fsMod.existsSync(filePath)) {
+ try {
+ const content = fsMod.readFileSync(filePath, 'utf-8');
+ const parsed = JSON.parse(content);
+ if (Array.isArray(parsed) && parsed.length > 0) {
+ return parsed;
+ }
+ } catch (e) {
+ console.warn('[DataService] Error leyendo archivo de productos persistidos:', e);
+ }
+ }
+ return [...INITIAL_PRODUCTS];
+}
+
+function savePersistedProducts(products: Product[]): void {
+ const fsMod = getFsModule();
+ const pathMod = getPathModule();
+ const filePath = getPersistedFilePath();
+ if (fsMod && pathMod && filePath) {
+ try {
+ const dir = pathMod.dirname(filePath);
+ if (!fsMod.existsSync(dir)) {
+ fsMod.mkdirSync(dir, { recursive: true });
+ }
+ fsMod.writeFileSync(filePath, JSON.stringify(products, null, 2), 'utf-8');
+ } catch (e) {
+ console.warn('[DataService] Error guardando productos en archivo persistente:', e);
+ }
+ }
+}
+
class DataService {
- private products: Product[] = [...INITIAL_PRODUCTS];
+ private products: Product[] = loadPersistedProducts();
private cities: City[] = [...INITIAL_CITIES];
private paymentMethods: PaymentMethod[] = [...INITIAL_PAYMENT_METHODS];
private suppliers: Supplier[] = [...INITIAL_SUPPLIERS];
@@ -197,9 +259,17 @@ class DataService {
private supplierPosts: SupplierPost[] = [...INITIAL_SUPPLIER_POSTS];
getProducts(): Product[] {
+ if (this.products.length === 0) {
+ this.products = loadPersistedProducts();
+ }
return groupProductsByModel(this.products);
}
+ setProducts(products: Product[]): void {
+ this.products = products;
+ savePersistedProducts(this.products);
+ }
+
getProductById(id: string): Product | undefined {
const allGrouped = groupProductsByModel(this.products);
return allGrouped.find((p) => p.id === id || p.colors?.some((c) => c.id === id));
@@ -274,6 +344,7 @@ class DataService {
if (!existing.description || existing.description.length < 50) {
existing.description = cleanDescription;
}
+ savePersistedProducts(this.products);
return existing;
}
@@ -306,6 +377,7 @@ class DataService {
};
this.products.unshift(formattedProduct);
+ savePersistedProducts(this.products);
return formattedProduct;
}
@@ -313,12 +385,14 @@ class DataService {
const idx = this.products.findIndex((p) => p.id === id);
if (idx === -1) return null;
this.products[idx] = { ...this.products[idx], ...updates };
+ savePersistedProducts(this.products);
return this.products[idx];
}
deleteProduct(id: string): boolean {
const initialLen = this.products.length;
this.products = this.products.filter((p) => p.id !== id);
+ savePersistedProducts(this.products);
return this.products.length < initialLen;
}
diff --git a/dripzone-app/src/lib/evolution-api.ts b/dripzone-app/src/lib/evolution-api.ts
index a8a640d..6976842 100644
--- a/dripzone-app/src/lib/evolution-api.ts
+++ b/dripzone-app/src/lib/evolution-api.ts
@@ -113,18 +113,33 @@ export async function notifyNewProductPublished(
`━━━━━━━━━━━━━━━━━━━━━━`
);
+ // Resolver URL absoluta de la imagen para garantizar que Evolution API la descargue con éxito
+ let resolvedMediaUrl = mediaUrl;
+ if (!resolvedMediaUrl && product?.images && product.images.length > 0) {
+ const rawImg = product.images[0];
+ if (rawImg.startsWith('http://') || rawImg.startsWith('https://')) {
+ resolvedMediaUrl = rawImg;
+ } else if (rawImg.startsWith('/')) {
+ resolvedMediaUrl = `https://dripzone.almeira.com.co${rawImg}`;
+ } else {
+ resolvedMediaUrl = `https://dripzone.almeira.com.co/${rawImg}`;
+ }
+ } else if (resolvedMediaUrl && !resolvedMediaUrl.startsWith('http')) {
+ resolvedMediaUrl = `https://dripzone.almeira.com.co${resolvedMediaUrl.startsWith('/') ? '' : '/'}${resolvedMediaUrl}`;
+ }
+
const dispatchedTo: string[] = [];
for (const target of NOTIFICATION_TARGETS) {
try {
let sent = false;
- // Si tenemos URL de imagen completa y válida, intentamos enviar con imagen
- if (mediaUrl && (mediaUrl.startsWith('http://') || mediaUrl.startsWith('https://'))) {
- sent = await sendWhatsAppMedia(target, mediaUrl, notificationCaption);
+ // 1. Enviar prioritariamente con la foto del calzado en alta definición
+ if (resolvedMediaUrl && (resolvedMediaUrl.startsWith('http://') || resolvedMediaUrl.startsWith('https://'))) {
+ sent = await sendWhatsAppMedia(target, resolvedMediaUrl, notificationCaption);
}
- // Si no hay imagen o falló el envío multimedia, enviar como texto formateado de alta visibilidad
+ // 2. Si no hay imagen o falló el envío multimedia, enviar como texto formateado
if (!sent) {
sent = await sendWhatsAppMessage(target, notificationCaption);
}
@@ -180,7 +195,8 @@ export function buildOrderConfirmationMessage(order: any): string {
/**
* Notifica inmediatamente al administrador Carlos (+57 316 745 5877)
- * y al canal de notificaciones cuando entra un nuevo pedido en la plataforma (web o app móvil).
+ * y al canal de notificaciones cuando entra un nuevo pedido en la plataforma (web o app móvil),
+ * INCLUYENDO la fotografía del calzado en la notificación oficial.
*/
export async function notifyAdminNewOrder(order: any): Promise {
const CARLOS_PHONE = '573167455877';
@@ -221,24 +237,40 @@ export async function notifyAdminNewOrder(order: any): Promise {
`👉 https://dripzone.almeira.com.co/admin/pedidos`
);
+ // Extraer y resolver la URL absoluta de la fotografía del calzado
+ const firstItem = order.items?.[0];
+ const rawImage = firstItem?.image || (Array.isArray(firstItem?.images) ? firstItem.images[0] : null);
+ let resolvedImageUrl: string | null = null;
+ if (rawImage && typeof rawImage === 'string') {
+ if (rawImage.startsWith('http://') || rawImage.startsWith('https://')) {
+ resolvedImageUrl = rawImage;
+ } else if (rawImage.startsWith('/')) {
+ resolvedImageUrl = `https://dripzone.almeira.com.co${rawImage}`;
+ } else {
+ resolvedImageUrl = `https://dripzone.almeira.com.co/${rawImage}`;
+ }
+ }
+
let success = false;
try {
- // 1. Notificación principal al WhatsApp personal de Carlos
- const resCarlos = await sendWhatsAppMessage(CARLOS_PHONE, adminMessage);
- if (resCarlos) success = true;
-
- // 2. Si el producto principal tiene imagen web, enviar foto como soporte
- const firstImage = order.items?.[0]?.image || (Array.isArray(order.items?.[0]?.images) ? order.items[0].images[0] : null);
- if (firstImage && (firstImage.startsWith('http://') || firstImage.startsWith('https://'))) {
- await sendWhatsAppMedia(
- CARLOS_PHONE,
- firstImage,
- `👟 Foto de referencia: ${order.items[0]?.productName || order.items[0]?.name || 'Producto DripZone'}`
- ).catch(() => {});
+ // 1. Notificación principal al WhatsApp personal de Carlos CON LA FOTO DEL PRODUCTO
+ let sentCarlos = false;
+ if (resolvedImageUrl) {
+ sentCarlos = await sendWhatsAppMedia(CARLOS_PHONE, resolvedImageUrl, adminMessage);
}
+ if (!sentCarlos) {
+ sentCarlos = await sendWhatsAppMessage(CARLOS_PHONE, adminMessage);
+ }
+ if (sentCarlos) success = true;
- // 3. Notificación al canal de alertas
- await sendWhatsAppMessage(NOTIFICATION_GROUP, adminMessage).catch(() => {});
+ // 2. Notificación al canal de alertas (grupo) CON LA FOTO DEL PRODUCTO
+ let sentGroup = false;
+ if (resolvedImageUrl) {
+ sentGroup = await sendWhatsAppMedia(NOTIFICATION_GROUP, resolvedImageUrl, adminMessage).catch(() => false);
+ }
+ if (!sentGroup) {
+ await sendWhatsAppMessage(NOTIFICATION_GROUP, adminMessage).catch(() => {});
+ }
} catch (err: any) {
console.error('Error enviando notificación de pedido a Carlos:', err.message);
}