feat(adsense): implementar Anchor Ads y Vignette Ads

- Anchor Ads: anuncios fijos top/bottom con botones minimizar/cerrar
- Vignette Ads: modal fullscreen con triggers configurables
- Schema v1.3.0 con grupos anchor_ads y vignette_ads (18 campos)
- FieldMapper actualizado para persistir settings en BD
- JavaScript para interacción (colapso, cierre, localStorage)
- Soporte para responsive y tamaños fijos en vignette

IMPORTANTE: Ejecutar en servidor remoto:
wp roi-theme sync-component adsense-placement

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
FrankZamora
2025-11-28 21:00:00 -06:00
parent 4d5cc1a58c
commit b96a13427e
6 changed files with 1313 additions and 5 deletions

View File

@@ -54,6 +54,8 @@ final class AdsensePlacementFormBuilder
$html .= $this->buildCredentialsGroup($componentId);
$html .= $this->buildAnalyticsGroup($componentId);
$html .= $this->buildRailAdsGroup($componentId);
$html .= $this->buildAnchorAdsGroup($componentId);
$html .= $this->buildVignetteAdsGroup($componentId);
$html .= $this->buildExclusionsGroup($componentId);
$html .= ' </div>';
@@ -114,6 +116,11 @@ final class AdsensePlacementFormBuilder
// Diagrama visual del layout
$html .= '<div class="border rounded p-3" style="background: #f8f9fa; font-family: monospace; font-size: 11px;">';
// Anchor Top
$html .= '<div class="text-center p-2 mb-1 rounded" style="background: #d1ecf1; border: 2px solid #17a2b8;">';
$html .= ' <i class="bi bi-pin-angle"></i> <strong>ANCHOR TOP</strong> (fijo, collapsible)';
$html .= '</div>';
// Header
$html .= '<div class="text-center p-2 mb-1 rounded" style="background: #e9ecef; border: 1px dashed #6c757d;">';
$html .= ' <strong>HEADER</strong>';
@@ -169,10 +176,15 @@ final class AdsensePlacementFormBuilder
$html .= '</div>';
// Footer
$html .= '<div class="text-center p-2 rounded" style="background: #e9ecef; border: 1px dashed #6c757d;">';
$html .= '<div class="text-center p-2 mb-1 rounded" style="background: #e9ecef; border: 1px dashed #6c757d;">';
$html .= ' <strong>FOOTER</strong>';
$html .= '</div>';
// Anchor Bottom
$html .= '<div class="text-center p-2 rounded" style="background: #d1ecf1; border: 2px solid #17a2b8;">';
$html .= ' <i class="bi bi-pin-angle"></i> <strong>ANCHOR BOTTOM</strong> (fijo, collapsible)';
$html .= '</div>';
// Rail Ads (laterales)
$html .= '<div class="mt-2 d-flex justify-content-between">';
$html .= ' <div class="p-2 rounded text-center" style="width: 45%; background: #f8d7da; border: 2px solid #dc3545; font-size: 10px;">';
@@ -183,11 +195,19 @@ final class AdsensePlacementFormBuilder
$html .= ' </div>';
$html .= '</div>';
// Vignette Ad (modal)
$html .= '<div class="mt-2 p-2 rounded text-center" style="background: #f3e5f5; border: 2px solid #9c27b0;">';
$html .= ' <i class="bi bi-fullscreen"></i> <strong>VIGNETTE</strong> (modal pantalla completa)';
$html .= ' <br><small class="text-muted">Aparece segun trigger configurado</small>';
$html .= '</div>';
$html .= '</div>';
$html .= '<div class="mt-2 small text-muted">';
$html .= ' <i class="bi bi-info-circle"></i> Los anuncios <span class="badge bg-warning text-dark">amarillos</span> son configurables abajo.';
$html .= ' Los <span class="badge bg-danger">rojos</span> solo aparecen en pantallas &gt;1600px.';
$html .= ' <i class="bi bi-info-circle"></i> <span class="badge bg-warning text-dark">Amarillo</span> = Posts, ';
$html .= ' <span class="badge bg-danger">Rojo</span> = Rails &gt;1600px, ';
$html .= ' <span class="badge" style="background:#17a2b8;color:white;">Cyan</span> = Anchors, ';
$html .= ' <span class="badge" style="background:#9c27b0;color:white;">Morado</span> = Vignette';
$html .= '</div>';
$html .= ' </div>';
@@ -506,6 +526,177 @@ final class AdsensePlacementFormBuilder
return $html;
}
/**
* Seccion para Anchor Ads (anuncios fijos top/bottom)
*/
private function buildAnchorAdsGroup(string $cid): string
{
$html = '<div class="card shadow-sm mb-3" style="border-left: 4px solid #17a2b8;">';
$html .= ' <div class="card-body">';
$html .= ' <h5 class="fw-bold mb-3" style="color: #1e3a5f;">';
$html .= ' <i class="bi bi-pin-angle me-2" style="color: #17a2b8;"></i>';
$html .= ' Anuncios Fijos (Anchor)';
$html .= ' </h5>';
$html .= ' <p class="small text-muted mb-3">Anuncios fijos en el borde superior o inferior de la pantalla.</p>';
// Master switch
$anchorEnabled = $this->renderer->getFieldValue($cid, 'anchor_ads', 'anchor_enabled', false);
$html .= $this->buildSwitch($cid . 'AnchorEnabled', 'Activar Anchor Ads', $anchorEnabled, 'bi-power');
// Posicion
$anchorPosition = $this->renderer->getFieldValue($cid, 'anchor_ads', 'anchor_position', 'bottom');
$html .= $this->buildSelect($cid . 'AnchorPosition', 'Posicion del anuncio',
$anchorPosition,
[
'top' => 'Solo en la parte superior',
'bottom' => 'Solo en la parte inferior',
'both' => 'Superior e inferior'
]
);
// Altura
$anchorHeight = $this->renderer->getFieldValue($cid, 'anchor_ads', 'anchor_height', '90');
$html .= $this->buildSelect($cid . 'AnchorHeight', 'Altura del anchor',
$anchorHeight,
['50' => '50px', '90' => '90px', '100' => '100px', '120' => '120px']
);
// Collapsible toggle
$collapsible = $this->renderer->getFieldValue($cid, 'anchor_ads', 'anchor_collapsible_enabled', true);
$html .= $this->buildSwitch($cid . 'AnchorCollapsibleEnabled', 'Permitir minimizar', $collapsible, 'bi-arrows-collapse');
$html .= '<small class="text-muted d-block" style="margin-top: -8px; margin-left: 40px;">Usuario puede minimizar en lugar de cerrar</small>';
// Pantallas
$html .= '<div class="row g-2 mt-2">';
$html .= ' <div class="col-6">';
$showMobile = $this->renderer->getFieldValue($cid, 'anchor_ads', 'anchor_show_on_mobile', true);
$html .= $this->buildSwitch($cid . 'AnchorShowOnMobile', 'Mostrar en movil', $showMobile, 'bi-phone');
$html .= ' </div>';
$html .= ' <div class="col-6">';
$showWide = $this->renderer->getFieldValue($cid, 'anchor_ads', 'anchor_show_on_wide_screens', false);
$html .= $this->buildSwitch($cid . 'AnchorShowOnWideScreens', 'Pantallas anchas', $showWide, 'bi-display');
$html .= ' </div>';
$html .= '</div>';
// Recordar estado
$html .= '<div class="mt-3 p-2 rounded" style="background: #e7f1ff;">';
$rememberState = $this->renderer->getFieldValue($cid, 'anchor_ads', 'anchor_remember_state', true);
$html .= $this->buildSwitch($cid . 'AnchorRememberState', 'Recordar cierre/colapso', $rememberState, 'bi-clock-history');
$rememberDuration = $this->renderer->getFieldValue($cid, 'anchor_ads', 'anchor_remember_duration', 'session');
$html .= $this->buildSelect($cid . 'AnchorRememberDuration', 'Duracion',
$rememberDuration,
[
'session' => 'Solo esta sesion',
'1hour' => '1 hora',
'1day' => '1 dia',
'1week' => '1 semana'
]
);
$html .= '</div>';
$html .= ' </div>';
$html .= '</div>';
return $html;
}
/**
* Seccion para Vignette Ads (pantalla completa)
*/
private function buildVignetteAdsGroup(string $cid): string
{
$html = '<div class="card shadow-sm mb-3" style="border-left: 4px solid #9c27b0;">';
$html .= ' <div class="card-body">';
$html .= ' <h5 class="fw-bold mb-3" style="color: #1e3a5f;">';
$html .= ' <i class="bi bi-fullscreen me-2" style="color: #9c27b0;"></i>';
$html .= ' Anuncios de Vineta';
$html .= ' <span class="badge bg-secondary ms-2">Pantalla Completa</span>';
$html .= ' </h5>';
$html .= ' <p class="small text-muted mb-3">Anuncios que ocupan toda la pantalla, aparecen segun el trigger configurado.</p>';
// Master switch
$vignetteEnabled = $this->renderer->getFieldValue($cid, 'vignette_ads', 'vignette_enabled', false);
$html .= $this->buildSwitch($cid . 'VignetteEnabled', 'Activar Vignette Ads', $vignetteEnabled, 'bi-power');
// Trigger
$vignetteTrigger = $this->renderer->getFieldValue($cid, 'vignette_ads', 'vignette_trigger', 'pageview');
$html .= $this->buildSelect($cid . 'VignetteTrigger', 'Cuando mostrar',
$vignetteTrigger,
[
'pageview' => 'Al cargar la pagina',
'scroll_50' => 'Al scrollear 50%',
'scroll_75' => 'Al scrollear 75%',
'exit_intent' => 'Al intentar salir',
'time_delay' => 'Despues de X segundos'
]
);
// Delay inicial
$triggerDelay = $this->renderer->getFieldValue($cid, 'vignette_ads', 'vignette_trigger_delay', '5');
$html .= $this->buildTextInput($cid . 'VignetteTriggerDelay', 'Delay inicial (segundos)', (string)$triggerDelay, '5');
// Tamano y opacidad
$html .= '<div class="row g-2 mt-2">';
$html .= ' <div class="col-6">';
$size = $this->renderer->getFieldValue($cid, 'vignette_ads', 'vignette_size', '300x250');
$html .= $this->buildSelect($cid . 'VignetteSize', 'Tamano',
$size,
['300x250' => '300x250', '336x280' => '336x280', 'responsive' => 'Responsive']
);
$html .= ' </div>';
$html .= ' <div class="col-6">';
$opacity = $this->renderer->getFieldValue($cid, 'vignette_ads', 'vignette_overlay_opacity', '0.7');
$html .= $this->buildSelect($cid . 'VignetteOverlayOpacity', 'Opacidad fondo',
$opacity,
['0.5' => '50%', '0.6' => '60%', '0.7' => '70%', '0.8' => '80%', '0.9' => '90%']
);
$html .= ' </div>';
$html .= '</div>';
// Pantallas
$html .= '<div class="row g-2 mt-2">';
$html .= ' <div class="col-6">';
$showMobile = $this->renderer->getFieldValue($cid, 'vignette_ads', 'vignette_show_on_mobile', true);
$html .= $this->buildSwitch($cid . 'VignetteShowOnMobile', 'Mostrar en movil', $showMobile, 'bi-phone');
$html .= ' </div>';
$html .= ' <div class="col-6">';
$showDesktop = $this->renderer->getFieldValue($cid, 'vignette_ads', 'vignette_show_on_desktop', true);
$html .= $this->buildSwitch($cid . 'VignetteShowOnDesktop', 'Mostrar en desktop', $showDesktop, 'bi-display');
$html .= ' </div>';
$html .= '</div>';
// Reaparicion
$html .= '<div class="mt-3 p-2 rounded" style="background: #f3e5f5;">';
$html .= '<p class="small fw-semibold mb-2"><i class="bi bi-arrow-repeat me-1"></i> Reaparicion</p>';
$reshowEnabled = $this->renderer->getFieldValue($cid, 'vignette_ads', 'vignette_reshow_enabled', true);
$html .= $this->buildSwitch($cid . 'VignetteReshowEnabled', 'Permitir reaparicion', $reshowEnabled);
$html .= '<div class="row g-2">';
$html .= ' <div class="col-6">';
$reshowTime = $this->renderer->getFieldValue($cid, 'vignette_ads', 'vignette_reshow_time', '5');
$html .= $this->buildSelect($cid . 'VignetteReshowTime', 'Tiempo (min)',
$reshowTime,
['1' => '1 min', '2' => '2 min', '3' => '3 min', '4' => '4 min', '5' => '5 min', '10' => '10 min', '15' => '15 min', '30' => '30 min']
);
$html .= ' </div>';
$html .= ' <div class="col-6">';
$maxSession = $this->renderer->getFieldValue($cid, 'vignette_ads', 'vignette_max_per_session', '3');
$html .= $this->buildSelect($cid . 'VignetteMaxPerSession', 'Max/sesion',
$maxSession,
['1' => '1', '2' => '2', '3' => '3', '5' => '5', 'unlimited' => 'Sin limite']
);
$html .= ' </div>';
$html .= '</div>';
$html .= '</div>';
$html .= ' </div>';
$html .= '</div>';
return $html;
}
private function buildExclusionsGroup(string $cid): string
{
$html = '<div class="card shadow-sm mb-3" style="border-left: 4px solid #6c757d;">';