feat: Rediseño de Inteligencia Comercial a formato listado con cupones integrados

This commit is contained in:
2026-03-04 21:07:35 -05:00
parent 52cc20e81b
commit 1488af32ae

View File

@ -206,43 +206,92 @@
<span class="material-icons">ads_click</span>
<h2>Impacto de Aliados Comerciales</h2>
</div>
<div class="data-table-wrapper">
<table class="data-table">
<thead>
<tr>
<th>Negocio</th>
<th>Visitas</th>
<th>Interacciones (R/LL/M)</th>
<th>Cupones</th>
<th>Favoritos</th>
<th>Salud</th>
</tr>
</thead>
<tbody>
<tr v-for="(data, name) in stats.businesses" :key="name">
<td class="id-cell">{{ name }}</td>
<td>{{ data.views }}</td>
<td>
<div style="display:flex; gap:8px; font-size:0.75rem; color:#cbd5e1;">
<span title="Click en Redes Sociales">S: {{ data.social }}</span>
<span title="Llamadas Realizadas">L: {{ data.calls }}</span>
<span title="Visitas a Maps">M: {{ data.location }}</span>
</div>
</td>
<td>{{ data.promos }}</td>
<td>
<span style="display:flex; align-items:center; gap:2px;">
<span class="material-icons" style="font-size:14px; color:#e91e63;">favorite</span> {{ data.favs }}
</span>
</td>
<td>
<div class="business-list">
<div v-for="(data, name) in stats.businesses" :key="name" class="business-card-analytics">
<!-- Business Header -->
<div class="business-header">
<div class="business-title-info">
<div class="biz-icon-box"><span class="material-icons">storefront</span></div>
<h3>{{ name }}</h3>
</div>
<div class="business-total-badge">
<span class="material-icons">data_exploration</span>
<span><b>{{ data.views + data.social + data.calls + data.location + data.promos + data.favs }}</b> Interacciones</span>
</div>
</div>
<!-- Details Grid -->
<div class="business-details-grid">
<div class="detail-item">
<span class="material-icons" style="color:#cbd5e1">visibility</span>
<div class="detail-info">
<span class="detail-value">{{ data.views }}</span>
<span class="detail-label">Vistas del Local</span>
</div>
</div>
<div class="detail-item">
<span class="material-icons" style="color:#25D366">chat</span>
<div class="detail-info">
<span class="detail-value">{{ data.social }}</span>
<span class="detail-label">Redes / WP</span>
</div>
</div>
<div class="detail-item">
<span class="material-icons" style="color:#f87171">phone</span>
<div class="detail-info">
<span class="detail-value">{{ data.calls }}</span>
<span class="detail-label">Llamadas Directas</span>
</div>
</div>
<div class="detail-item">
<span class="material-icons" style="color:#60a5fa">place</span>
<div class="detail-info">
<span class="detail-value">{{ data.location }}</span>
<span class="detail-label">Usos del Mapa</span>
</div>
</div>
<div class="detail-item">
<span class="material-icons" style="color:#e91e63">favorite</span>
<div class="detail-info">
<span class="detail-value">{{ data.favs }}</span>
<span class="detail-label">Veces Favorito</span>
</div>
</div>
</div>
<!-- Coupons / Promos Summary -->
<div class="business-coupons-section" :class="{ 'has-coupons': Object.keys(data.coupons || {}).length > 0 }">
<div class="coupons-header">
<div style="display:flex; align-items:center; gap: 8px;">
<span class="material-icons" style="color:#fee715">confirmation_number</span>
<h4>Tráfico por Promociones</h4>
</div>
<div class="status-pill-wrap">
<span class="status-pill" :class="getHealthClass(calculateConversion(data.views, data.promos))">
{{ getHealthLabel(calculateConversion(data.views, data.promos)) }}
Salud: {{ getHealthLabel(calculateConversion(data.views, data.promos)) }}
</span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div v-if="Object.keys(data.coupons || {}).length > 0" class="coupon-list">
<div v-for="(couponData, couponName) in data.coupons" :key="couponName" class="coupon-item">
<div class="coupon-name-box">
<span class="material-icons" style="font-size:14px; color:var(--text-secondary)">local_offer</span>
<span class="coupon-name">{{ couponName }}</span>
</div>
<div class="coupon-stats">
<span class="stat" title="Clicks a la promo"><span class="material-icons">visibility</span> {{ couponData.views }}</span>
<span class="stat" title="Clicks al mapa desde promo"><span class="material-icons">place</span> {{ couponData.location }}</span>
</div>
</div>
</div>
<div v-else class="no-coupons">
<span class="material-icons">info</span> No hay promociones generadas.
</div>
</div>
</div>
</div>
</section>
</div>
@ -442,7 +491,7 @@ onMounted(async () => {
if (allBusinesses) {
for (const b of allBusinesses) {
if (b.name) {
bizStats[b.name] = { views: 0, promos: 0, favs: 0, social: 0, location: 0, calls: 0 }
bizStats[b.name] = { views: 0, promos: 0, favs: 0, social: 0, location: 0, calls: 0, coupons: {} }
}
}
}
@ -478,7 +527,7 @@ onMounted(async () => {
} else if (ev.entity_type === 'business') {
if (!bizStats[nameKey]) {
bizStats[nameKey] = { views: 0, promos: 0, favs: 0, social: 0, location: 0, calls: 0 }
bizStats[nameKey] = { views: 0, promos: 0, favs: 0, social: 0, location: 0, calls: 0, coupons: {} }
}
if (ev.event_name === 'view_details') {
@ -500,14 +549,20 @@ onMounted(async () => {
// Los cupones se suman a la parte del negocio correspondiente
const bizName = ev.properties?.business || nameKey
if (!bizStats[bizName]) {
bizStats[bizName] = { views: 0, promos: 0, favs: 0, social: 0, location: 0, calls: 0 }
bizStats[bizName] = { views: 0, promos: 0, favs: 0, social: 0, location: 0, calls: 0, coupons: {} }
}
if (!bizStats[bizName].coupons[nameKey]) {
bizStats[bizName].coupons[nameKey] = { views: 0, location: 0 }
}
if (ev.event_name === 'promo_view') {
bizStats[bizName].promos++
bizStats[bizName].coupons[nameKey].views++
total_promo_clicks++
} else if (ev.event_name === 'location_click') {
bizStats[bizName].location++
bizStats[bizName].coupons[nameKey].location++
}
}
}
@ -604,6 +659,38 @@ h1 { font-size: 2.2rem; font-weight: 900; margin: 0; }
.status-pill.good { background: rgba(59, 130, 246, 0.1); color: #3b82f6; }
.status-pill.low { background: rgba(244, 63, 94, 0.1); color: #f43f5e; }
/* BUSINESS LIST CARDS */
.business-list { display: flex; flex-direction: column; gap: 20px; }
.business-card-analytics { background: var(--card-bg); border-radius: 20px; border: 1px solid var(--border-color); padding: 24px; box-shadow: 0 10px 30px rgba(0,0,0,0.05); }
.business-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 24px; padding-bottom: 16px; border-bottom: 1px solid var(--border-color); }
.business-title-info { display: flex; align-items: center; gap: 12px; }
.biz-icon-box { background: rgba(254,231,21,0.1); color: #fee715; width: 44px; height: 44px; border-radius: 12px; display: flex; justify-content: center; align-items: center; }
.business-title-info h3 { margin: 0; font-size: 1.3rem; font-weight: 800; color: var(--text-primary); }
.business-total-badge { background: #1e293b; color: white; padding: 6px 14px; border-radius: 20px; display: flex; align-items: center; gap: 6px; font-size: 0.85rem; }
.business-total-badge .material-icons { font-size: 16px; color: #fee715; }
.business-details-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 16px; margin-bottom: 24px; }
.detail-item { display: flex; align-items: center; gap: 12px; background: var(--bg-secondary); padding: 12px 14px; border-radius: 14px; border: 1px solid var(--border-color); }
.detail-item .material-icons { font-size: 20px; }
.detail-info { display: flex; flex-direction: column; }
.detail-value { font-size: 1.2rem; font-weight: 900; color: var(--text-primary); }
.detail-label { font-size: 0.65rem; color: var(--text-secondary); text-transform: uppercase; font-weight: 700; margin-top:2px; }
.business-coupons-section { background: var(--bg-secondary); border-radius: 16px; padding: 20px; border: 1px solid var(--border-color); }
.business-coupons-section.has-coupons { border-color: rgba(254,231,21,0.3); background: rgba(254,231,21,0.02); }
.coupons-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px; }
.business-coupons-section h4 { margin: 0; font-size: 1rem; font-weight: 800; color: var(--text-primary); }
.coupon-list { display: flex; flex-direction: column; gap: 10px; }
.coupon-item { display: flex; justify-content: space-between; align-items: center; background: var(--card-bg); padding: 12px 16px; border-radius: 12px; border: 1px solid var(--border-color); box-shadow: 0 2px 10px rgba(0,0,0,0.05); }
.coupon-name-box { display: flex; align-items: center; gap: 8px; }
.coupon-name { font-weight: 700; font-size: 0.9rem; color: var(--text-primary); }
.coupon-stats { display: flex; gap: 16px; font-size: 0.85rem; font-weight: 700; color: var(--text-primary); }
.coupon-stats .stat { display: flex; align-items: center; gap: 4px; background: var(--bg-secondary); padding: 4px 10px; border-radius: 8px;}
.coupon-stats .stat .material-icons { font-size: 14px; color: var(--text-secondary); }
.no-coupons { font-size: 0.85rem; color: var(--text-secondary); display:flex; align-items:center; gap:8px; font-weight:600;}
/* ANIMATIONS */
.animate-fade { animation: fadeIn 0.4s ease-out; }
@keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }