34849-vm/marketing_produccion.php
2026-07-07 01:55:09 +00:00

1320 lines
62 KiB
PHP

<?php
$pageTitle = "Producción de Video";
include 'db/config.php';
include 'layout_header.php';
require_once 'includes/marketing_bootstrap.php';
$db = db();
marketing_ensure_schema($db);
// Obtener productos para el select
$stmt_products = $db->query("SELECT id, nombre FROM products ORDER BY nombre ASC");
$productos = $stmt_products->fetchAll(PDO::FETCH_ASSOC);
// Obtener videos existentes
$stmt_videos = $db->query("SELECT mv.*, p.nombre as nombre_producto
FROM marketing_videos mv
LEFT JOIN products p ON mv.producto_id = p.id
ORDER BY mv.orden ASC, mv.fecha_creacion DESC");
$videos = $stmt_videos->fetchAll(PDO::FETCH_ASSOC);
$statusOptions = ['PENDIENTE', 'EN PROCESO', 'TERMINADO', 'OBSERVADO', 'APROBADO', 'PUBLICADO'];
function marketing_status_badge_class(?string $estado): string
{
return match ($estado) {
'PENDIENTE' => 'bg-warning text-dark',
'EN PROCESO' => 'bg-info text-white',
'TERMINADO' => 'bg-primary text-white',
'OBSERVADO' => 'bg-danger text-white',
'APROBADO' => 'bg-success text-white',
'PUBLICADO' => 'bg-dark text-white',
default => 'bg-secondary text-white',
};
}
// Contadores de estado
$stats = [
'TOTAL' => count($videos),
'PENDIENTE' => 0,
'EN PROCESO' => 0,
'TERMINADO' => 0,
'APROBADO' => 0
];
foreach ($videos as $v) {
if (isset($stats[$v['estado']])) {
$stats[$v['estado']]++;
}
}
?>
<style>
:root {
--primary-color: #4361ee;
--secondary-color: #4cc9f0;
--success-color: #4cc9f0;
--info-color: #4895ef;
--warning-color: #f72585;
}
body {
background-color: #f8f9fa;
}
.table-excel {
font-size: 0.9rem;
}
.table-excel th {
background-color: #f8f9fa;
border-bottom: 2px solid #dee2e6;
white-space: nowrap;
text-transform: uppercase;
font-size: 0.85rem;
letter-spacing: 0.8px;
font-weight: 800;
color: #495057;
padding: 18px 12px;
vertical-align: middle;
}
.table-excel td {
vertical-align: middle;
border-bottom: 1px solid #f1f3f5;
padding: 12px 10px;
background-color: #fff;
}
.img-preview {
width: 120px;
height: 120px;
object-fit: cover;
border-radius: 12px;
cursor: pointer;
box-shadow: 0 4px 12px rgba(0,0,0,0.12);
transition: all 0.4s cubic-bezier(0.165, 0.84, 0.44, 1);
}
.img-preview:hover {
transform: scale(2.2);
z-index: 100;
position: relative;
box-shadow: 0 15px 30px rgba(0,0,0,0.2);
}
/* Estilos para Vista de Tarjetas */
#cardsView {
display: none;
}
.video-card {
border: none;
border-radius: 15px;
overflow: hidden;
transition: all 0.3s ease;
background: #fff;
height: 100%;
box-shadow: 0 4px 15px rgba(0,0,0,0.05);
}
.video-card:hover {
transform: translateY(-10px);
box-shadow: 0 15px 30px rgba(0,0,0,0.1);
}
.video-card-img-container {
position: relative;
height: 220px;
overflow: hidden;
background: #f8f9fa;
}
.video-card-img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.5s ease;
}
.video-card:hover .video-card-img {
transform: scale(1.1);
}
.video-card-status {
position: absolute;
top: 15px;
right: 15px;
z-index: 2;
}
.video-card-body {
padding: 1.5rem;
}
.video-card-title {
font-weight: 700;
font-size: 1.1rem;
margin-bottom: 0.5rem;
color: #2b2d42;
}
.video-card-info {
font-size: 0.85rem;
color: #6c757d;
margin-bottom: 0.5rem;
display: flex;
align-items: center;
}
.video-card-info i {
width: 20px;
color: #4361ee;
}
.video-card-footer {
padding: 1.25rem 1.5rem;
background: #f8f9fc;
border-top: 1px solid #edf2f7;
display: flex;
justify-content: space-between;
align-items: center;
}
.btn-view-toggle {
padding: 0.5rem 1rem;
border-radius: 10px;
font-weight: 600;
transition: all 0.2s;
}
.btn-view-toggle.active {
background-color: #4361ee;
color: white;
box-shadow: 0 4px 10px rgba(67, 97, 238, 0.3);
}
.link-icon {
font-size: 1.35rem;
transition: transform 0.2s;
}
.link-icon:hover {
transform: translateY(-2px);
}
.text-wrap-cell {
white-space: normal !important;
word-break: break-word;
line-height: 1.4;
min-width: 180px;
}
.status-badge {
font-size: 0.7rem;
padding: 0.5em 1em;
border-radius: 50px;
font-weight: 600;
letter-spacing: 0.3px;
}
.status-select {
min-width: 160px;
border: 0;
border-radius: 999px;
font-size: 0.78rem;
font-weight: 700;
letter-spacing: 0.35px;
text-transform: uppercase;
text-align: center;
text-align-last: center;
padding: 0.55rem 2.25rem 0.55rem 0.9rem;
box-shadow: inset 0 0 0 1px rgba(15, 23, 42, 0.06);
transition: transform 0.15s ease, box-shadow 0.15s ease, opacity 0.15s ease;
}
.status-select:hover {
transform: translateY(-1px);
box-shadow: 0 8px 16px rgba(15, 23, 42, 0.08);
}
.status-select:focus {
box-shadow: 0 0 0 0.2rem rgba(67, 97, 238, 0.16);
}
.status-select:disabled {
opacity: 0.8;
cursor: wait;
}
.status-select[data-status="PENDIENTE"] {
background-color: #ffe082;
color: #5f4b00;
}
.status-select[data-status="EN PROCESO"] {
background-color: #d8f3ff;
color: #0c5460;
}
.status-select[data-status="TERMINADO"] {
background-color: #dce7ff;
color: #1d4ed8;
}
.status-select[data-status="OBSERVADO"] {
background-color: #ffe0e6;
color: #b42318;
}
.status-select[data-status="APROBADO"] {
background-color: #dcfce7;
color: #166534;
}
.status-select[data-status="PUBLICADO"] {
background-color: #dfe4ea;
color: #111827;
}
.status-feedback {
min-height: 18px;
}
.editable:hover {
background-color: #f8f9fa;
cursor: pointer;
}
.observation-input {
min-width: 240px;
resize: vertical;
border-radius: 10px;
}
.observation-input:focus {
border-color: rgba(67, 97, 238, 0.35);
box-shadow: 0 0 0 0.2rem rgba(67, 97, 238, 0.12);
}
.observation-status {
min-height: 18px;
}
.card-stats {
border: none;
border-radius: 15px;
transition: transform 0.3s, box-shadow 0.3s;
}
.card-stats:hover {
transform: translateY(-5px);
box-shadow: 0 10px 20px rgba(0,0,0,0.05) !important;
}
.stat-icon {
width: 40px;
height: 40px;
border-radius: 10px;
display: flex; align-items: center; justify-content: center;
font-size: 1.2rem;
margin-bottom: 10px;
}
.card-link-badge {
width: 35px;
height: 35px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 10px;
font-size: 1.1rem;
}
/* Estilos para Cuadros (Tags) */
.tag-container {
display: flex;
flex-wrap: wrap;
gap: 6px;
padding: 4px 0;
}
.tag-item {
background: #ffffff;
border: 1px solid #e2e8f0;
color: #475569;
padding: 4px 10px;
border-radius: 8px;
font-size: 0.75rem;
font-weight: 700;
display: inline-block;
box-shadow: 0 2px 4px rgba(0,0,0,0.04);
transition: all 0.2s;
max-width: 100%;
word-break: break-word;
}
.tag-item:hover {
transform: translateY(-1px);
box-shadow: 0 4px 6px rgba(0,0,0,0.08);
}
.tag-item-material {
border-left: 4px solid #4361ee;
background-color: #f8faff;
}
.tag-item-angulo {
border-left: 4px solid #f72585;
background-color: #fff5f9;
}
</style>
<div class="row mb-4 g-3">
<div class="col-md-2">
<div class="card card-stats shadow-sm h-100">
<div class="card-body">
<div class="stat-icon bg-primary text-white"><i class="fas fa-list"></i></div>
<h6 class="text-muted mb-1 small">Total Pedidos</h6>
<h3 class="mb-0 fw-bold"><?php echo $stats['TOTAL']; ?></h3>
</div>
</div>
</div>
<div class="col-md-2">
<div class="card card-stats shadow-sm h-100">
<div class="card-body">
<div class="stat-icon bg-warning text-white"><i class="fas fa-clock"></i></div>
<h6 class="text-muted mb-1 small">Pendientes</h6>
<h3 class="mb-0 fw-bold" id="status-count-pendiente"><?php echo $stats['PENDIENTE']; ?></h3>
</div>
</div>
</div>
<div class="col-md-2">
<div class="card card-stats shadow-sm h-100">
<div class="card-body">
<div class="stat-icon bg-info text-white"><i class="fas fa-sync"></i></div>
<h6 class="text-muted mb-1 small">En Proceso</h6>
<h3 class="mb-0 fw-bold" id="status-count-en-proceso"><?php echo $stats['EN PROCESO']; ?></h3>
</div>
</div>
</div>
<div class="col-md-2">
<div class="card card-stats shadow-sm h-100">
<div class="card-body">
<div class="stat-icon bg-primary text-white" style="background-color: #4361ee !important;"><i class="fas fa-check-double"></i></div>
<h6 class="text-muted mb-1 small">Terminados</h6>
<h3 class="mb-0 fw-bold" id="status-count-terminado"><?php echo $stats['TERMINADO']; ?></h3>
</div>
</div>
</div>
<div class="col-md-2">
<div class="card card-stats shadow-sm h-100">
<div class="card-body">
<div class="stat-icon bg-success text-white" style="background-color: #2ec4b6 !important;"><i class="fas fa-thumbs-up"></i></div>
<h6 class="text-muted mb-1 small">Aprobados</h6>
<h3 class="mb-0 fw-bold" id="status-count-aprobado"><?php echo $stats['APROBADO']; ?></h3>
</div>
</div>
</div>
</div>
<div class="d-flex justify-content-between align-items-center mb-4">
<div>
<h2 class="mb-0 fw-bold" style="color: #2b2d42;">Producción de Video</h2>
<p class="text-muted small mb-1">Gestión de activos y flujo de trabajo creativo</p>
<p class="text-muted small mb-0">La columna <strong>Observación</strong> se guarda al salir del campo y <strong>Estado</strong> al cambiar la opción.</p>
</div>
<div class="d-flex align-items-center gap-3">
<div class="bg-white p-1 rounded-3 shadow-sm d-flex border">
<button class="btn btn-view-toggle active border-0" id="btnViewTable" onclick="switchView('table')">
<i class="fas fa-table me-1"></i> Tabla
</button>
<button class="btn btn-view-toggle border-0" id="btnViewCards" onclick="switchView('cards')">
<i class="fas fa-th-large me-1"></i> Tarjetas
</button>
</div>
<a href="marketing_assets.php" class="btn btn-light border shadow-sm">
<i class="fas fa-folder-open text-primary"></i> Biblioteca
</a>
<button type="button" class="btn btn-primary shadow-sm px-4" data-bs-toggle="modal" data-bs-target="#nuevoVideoModal">
<i class="fas fa-plus me-2"></i> Nuevo Pedido
</button>
</div>
</div>
<?php if (isset($_GET['success'])): ?>
<div class="alert alert-success alert-dismissible fade show" role="alert">
¡Operación realizada con éxito!
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<?php endif; ?>
<!-- Vista de Tabla -->
<div id="tableView" class="card shadow-sm border-0" style="border-radius: 15px; overflow: hidden;">
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover table-excel mb-0">
<thead>
<tr>
<th class="text-center" style="width: 60px;"><i class="fas fa-hashtag me-1"></i> #</th>
<th><i class="far fa-calendar-alt me-1"></i> Fecha Entrega</th>
<th><i class="fas fa-box me-1"></i> Producto</th>
<th class="text-center"><i class="fas fa-image me-1"></i> Imagen</th>
<th><i class="fas fa-layer-group me-1"></i> Material</th>
<th><i class="fas fa-video me-1"></i> Ángulo</th>
<th class="text-center"><i class="fas fa-link me-1"></i> Insp. Landing</th>
<th class="text-center"><i class="fab fa-youtube me-1"></i> Video Ref.</th>
<th class="text-center"><i class="fas fa-play-circle me-1"></i> Link Video</th>
<th class="text-center"><i class="fas fa-globe me-1"></i> Link Landing</th>
<th class="text-center"><i class="fas fa-file-image me-1"></i> Link Flyer</th>
<th><i class="fas fa-comment-dots me-1"></i> Observación</th>
<th class="text-center"><i class="fas fa-tasks me-1"></i> Estado</th>
<th class="text-center"><i class="fas fa-cog me-1"></i> Acción</th>
</tr>
</thead>
<tbody>
<?php if (empty($videos)): ?>
<tr>
<td colspan="14" class="text-center py-5 text-muted">
<i class="fas fa-video-slash fa-3x mb-3 d-block opacity-25"></i>
No hay pedidos registrados.
</td>
</tr>
<?php else: ?>
<?php foreach ($videos as $v): ?>
<tr>
<td class="text-center fw-bold text-primary"><?php echo $v['orden']; ?></td>
<td>
<div class="d-flex align-items-center">
<i class="far fa-calendar-alt me-2 text-muted"></i>
<?php echo $v['fecha_entrega'] ? date('d/m/Y', strtotime($v['fecha_entrega'])) : '<span class="text-muted">-</span>'; ?>
</div>
</td>
<td class="fw-bold text-dark"><?php echo htmlspecialchars($v['nombre_producto'] ?: 'General'); ?></td>
<td class="text-center">
<?php if ($v['foto_producto']): ?>
<img src="<?php echo $v['foto_producto']; ?>" class="img-preview" alt="Ref" onclick="window.open(this.src)">
<?php else: ?>
<div class="bg-light rounded d-flex align-items-center justify-content-center mx-auto" style="width: 120px; height: 120px; border-radius: 12px;">
<i class="fas fa-image text-muted opacity-50 fa-2x"></i>
</div>
<?php endif; ?>
</td>
<td class="editable text-wrap-cell" data-id="<?php echo $v['id']; ?>" data-field="material" data-value="<?php echo htmlspecialchars($v['material']); ?>">
<div class="tag-container">
<?php
$items = explode(',', $v['material']);
$hasItems = false;
foreach ($items as $item):
$item = trim($item);
if (!$item) continue;
$hasItems = true;
?>
<span class="tag-item tag-item-material"><?php echo htmlspecialchars($item); ?></span>
<?php endforeach; if (!$hasItems) echo '<span class="text-muted">-</span>'; ?>
</div>
</td>
<td class="editable text-wrap-cell" data-id="<?php echo $v['id']; ?>" data-field="angulo_video" data-value="<?php echo htmlspecialchars($v['angulo_video']); ?>">
<div class="tag-container">
<?php
$items = explode(',', $v['angulo_video']);
$hasItems = false;
foreach ($items as $item):
$item = trim($item);
if (!$item) continue;
$hasItems = true;
?>
<span class="tag-item tag-item-angulo"><?php echo htmlspecialchars($item); ?></span>
<?php endforeach; if (!$hasItems) echo '<span class="text-muted">-</span>'; ?>
</div>
</td>
<td class="text-center">
<?php if ($v['link_inspiracion_landing']):
$links = explode(',', $v['link_inspiracion_landing']);
foreach ($links as $link):
$link = trim($link);
if (!$link) continue;
?>
<a href="<?php echo $link; ?>" target="_blank" class="link-icon d-inline-block me-1 text-primary"><i class="fas fa-external-link-alt"></i></a>
<?php endforeach; ?>
<?php else: ?>
<span class="text-muted">-</span>
<?php endif; ?>
</td>
<td class="text-center">
<?php if ($v['link_inspiracion_video']):
$links = explode(',', $v['link_inspiracion_video']);
foreach ($links as $link):
$link = trim($link);
if (!$link) continue;
?>
<a href="<?php echo $link; ?>" target="_blank" class="link-icon text-danger d-inline-block me-1"><i class="fab fa-youtube"></i></a>
<?php endforeach; ?>
<?php else: ?>
<span class="text-muted">-</span>
<?php endif; ?>
</td>
<td class="text-center">
<?php if ($v['link_video']):
$links = explode(',', $v['link_video']);
foreach ($links as $link):
$link = trim($link);
if (!$link) continue;
?>
<a href="<?php echo $link; ?>" target="_blank" class="link-icon text-success d-inline-block me-1"><i class="fas fa-play-circle"></i></a>
<?php endforeach; ?>
<?php else: ?>
<span class="text-muted">-</span>
<?php endif; ?>
</td>
<td class="text-center">
<?php if ($v['link_landing']):
$links = explode(',', $v['link_landing']);
foreach ($links as $link):
$link = trim($link);
if (!$link) continue;
?>
<a href="<?php echo $link; ?>" target="_blank" class="link-icon text-info d-inline-block me-1"><i class="fas fa-globe"></i></a>
<?php endforeach; ?>
<?php else: ?>
<span class="text-muted">-</span>
<?php endif; ?>
</td>
<td class="text-center">
<?php if ($v['link_flyer']):
$links = explode(',', $v['link_flyer']);
foreach ($links as $link):
$link = trim($link);
if (!$link) continue;
?>
<a href="<?php echo $link; ?>" target="_blank" class="link-icon text-warning d-inline-block me-1"><i class="fas fa-file-image"></i></a>
<?php endforeach; ?>
<?php else: ?>
<span class="text-muted">-</span>
<?php endif; ?>
</td>
<td>
<textarea
class="form-control form-control-sm observation-input"
data-id="<?php echo $v['id']; ?>"
rows="3"
placeholder="Escribe una observación..."><?php echo htmlspecialchars($v['observacion'] ?? ''); ?></textarea>
<div class="small text-muted mt-1 observation-status" id="observation-status-<?php echo $v['id']; ?>"></div>
</td>
<td class="text-center">
<select
class="form-select form-select-sm status-select"
data-id="<?php echo $v['id']; ?>"
data-status="<?php echo htmlspecialchars($v['estado'] ?? ''); ?>"
aria-label="Estado del pedido <?php echo (int)$v['id']; ?>">
<?php foreach ($statusOptions as $statusOption): ?>
<option value="<?php echo htmlspecialchars($statusOption); ?>" <?php echo ($v['estado'] === $statusOption) ? 'selected' : ''; ?>>
<?php echo htmlspecialchars($statusOption); ?>
</option>
<?php endforeach; ?>
</select>
<div class="small mt-1 status-feedback" id="status-feedback-<?php echo $v['id']; ?>"></div>
</td>
<td class="text-center">
<div class="d-flex justify-content-center gap-1">
<button class="btn btn-sm btn-light border shadow-sm" data-video-id="<?php echo $v['id']; ?>" data-video-payload="<?php echo htmlspecialchars(json_encode($v, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT), ENT_QUOTES, 'UTF-8'); ?>" onclick="gestionarVideoFromButton(this)" title="Editar">
<i class="fas fa-edit text-primary"></i>
</button>
<button class="btn btn-sm btn-light border shadow-sm" onclick="eliminarVideo(<?php echo $v['id']; ?>)" title="Eliminar">
<i class="fas fa-trash-alt text-danger"></i>
</button>
</div>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
<!-- Vista de Tarjetas -->
<div id="cardsView" class="mb-5">
<div class="row g-4">
<?php foreach ($videos as $v): ?>
<div class="col-12 col-md-6 col-lg-4 col-xl-3">
<div class="video-card">
<div class="video-card-img-container">
<?php if ($v['foto_producto']): ?>
<img src="<?php echo $v['foto_producto']; ?>" class="video-card-img" alt="Producto">
<?php else: ?>
<div class="w-100 h-100 d-flex align-items-center justify-content-center text-muted">
<i class="fas fa-image fa-3x opacity-25"></i>
</div>
<?php endif; ?>
<div class="video-card-status">
<span class="badge status-badge video-card-status-badge <?php echo marketing_status_badge_class($v['estado']); ?> shadow-sm" data-video-id="<?php echo $v['id']; ?>">
<?php echo htmlspecialchars($v['estado']); ?>
</span>
</div>
</div>
<div class="video-card-body">
<div class="video-card-title text-truncate"><?php echo htmlspecialchars($v['nombre_producto'] ?: 'General'); ?></div>
<div class="video-card-info">
<i class="far fa-calendar-alt"></i>
<span>Entrega: <?php echo $v['fecha_entrega'] ? date('d/m/Y', strtotime($v['fecha_entrega'])) : '-'; ?></span>
</div>
<div class="video-card-info">
<i class="fas fa-hashtag"></i>
<span>Orden: <?php echo $v['orden']; ?></span>
</div>
<div class="mt-3">
<small class="text-muted d-block mb-2 fw-bold">MATERIAL:</small>
<div class="tag-container">
<?php
$items = explode(',', $v['material']);
$hasItems = false;
foreach ($items as $item):
$item = trim($item);
if (!$item) continue;
$hasItems = true;
?>
<span class="tag-item tag-item-material" style="font-size: 0.7rem;"><?php echo htmlspecialchars($item); ?></span>
<?php endforeach; if (!$hasItems) echo '<span class="text-muted small">No especificado</span>'; ?>
</div>
</div>
</div>
<div class="video-card-footer">
<div class="d-flex gap-2">
<?php if ($v['link_inspiracion_video']): ?>
<div class="card-link-badge text-danger" style="background: #fee2e2;" title="Video Referencia"><i class="fab fa-youtube"></i></div>
<?php endif; ?>
<?php if ($v['link_video']): ?>
<div class="card-link-badge text-success" style="background: #dcfce7;" title="Video Final"><i class="fas fa-play"></i></div>
<?php endif; ?>
</div>
<button class="btn btn-sm btn-primary rounded-pill px-3" data-video-id="<?php echo $v['id']; ?>" data-video-payload="<?php echo htmlspecialchars(json_encode($v, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT), ENT_QUOTES, 'UTF-8'); ?>" onclick="gestionarVideoFromButton(this)">
Gestionar
</button>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
<!-- Modal Nuevo Video -->
<div class="modal fade" id="nuevoVideoModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<form action="save_marketing_video.php" method="POST" enctype="multipart/form-data">
<div class="modal-header">
<h5 class="modal-title">Nuevo Pedido de Video</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="row g-3">
<div class="col-md-2">
<label class="form-label">Orden</label>
<input type="number" name="orden" class="form-control" value="1">
</div>
<div class="col-md-5">
<label class="form-label">Fecha Entrega</label>
<input type="date" name="fecha_entrega" class="form-control">
</div>
<div class="col-md-5">
<label class="form-label">Producto</label>
<select name="producto_id" class="form-select">
<option value="">Seleccionar producto...</option>
<?php foreach ($productos as $p): ?>
<option value="<?php echo $p['id']; ?>"><?php echo htmlspecialchars($p['nombre']); ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-6">
<label class="form-label">Imagen Referencia</label>
<input type="file" name="foto_producto" class="form-control" accept="image/*">
</div>
<div class="col-md-6">
<label class="form-label">Material</label>
<input type="text" name="material" class="form-control" placeholder="Ej: Acero, Plástico, etc.">
</div>
<div class="col-md-6">
<label class="form-label d-flex justify-content-between">
Ángulo del Video
<button type="button" class="btn btn-sm btn-link p-0 text-decoration-none" onclick="addTextInput('angulo_video_container', 'angulo_video[]', 'Ej: Cenital, 45 grados...')">
<i class="fas fa-plus-circle"></i> Añadir otro
</button>
</label>
<div id="angulo_video_container">
<div class="input-group mb-2">
<input type="text" name="angulo_video[]" class="form-control" placeholder="Ej: Cenital, 45 grados...">
<button class="btn btn-outline-danger" type="button" onclick="if(document.querySelectorAll('#angulo_video_container .input-group').length > 1) this.parentElement.remove()"><i class="fas fa-times"></i></button>
</div>
</div>
</div>
<div class="col-md-6">
<label class="form-label d-flex justify-content-between">
Link Inspiración Landing
<button type="button" class="btn btn-sm btn-link p-0 text-decoration-none" onclick="addLinkInput('links_insp_landing_container', 'link_inspiracion_landing[]')">
<i class="fas fa-plus-circle"></i> Añadir otro
</button>
</label>
<div id="links_insp_landing_container">
<div class="input-group mb-2">
<input type="url" name="link_inspiracion_landing[]" class="form-control" placeholder="https://...">
<button class="btn btn-outline-danger" type="button" onclick="if(document.querySelectorAll('#links_insp_landing_container .input-group').length > 1) this.parentElement.remove()"><i class="fas fa-times"></i></button>
</div>
</div>
</div>
<div class="col-md-6">
<label class="form-label d-flex justify-content-between">
Video Referencial
<button type="button" class="btn btn-sm btn-link p-0 text-decoration-none" onclick="addLinkInput('links_video_container', 'link_inspiracion_video[]')">
<i class="fas fa-plus-circle"></i> Añadir otro
</button>
</label>
<div id="links_video_container">
<div class="input-group mb-2">
<input type="url" name="link_inspiracion_video[]" class="form-control" placeholder="https://...">
<button class="btn btn-outline-danger" type="button" onclick="if(document.querySelectorAll('#links_video_container .input-group').length > 1) this.parentElement.remove()"><i class="fas fa-times"></i></button>
</div>
</div>
</div>
<div class="col-md-6">
<label class="form-label d-flex justify-content-between">
Link Video Final
<button type="button" class="btn btn-sm btn-link p-0 text-decoration-none" onclick="addLinkInput('links_video_final_container', 'link_video[]')">
<i class="fas fa-plus-circle"></i> Añadir otro
</button>
</label>
<div id="links_video_final_container">
<div class="input-group mb-2">
<input type="url" name="link_video[]" class="form-control" placeholder="https://...">
<button class="btn btn-outline-danger" type="button" onclick="if(document.querySelectorAll('#links_video_final_container .input-group').length > 1) this.parentElement.remove()"><i class="fas fa-times"></i></button>
</div>
</div>
</div>
<div class="col-md-6">
<label class="form-label d-flex justify-content-between">
Link de la Landing
<button type="button" class="btn btn-sm btn-link p-0 text-decoration-none" onclick="addLinkInput('links_landing_container', 'link_landing[]')">
<i class="fas fa-plus-circle"></i> Añadir otro
</button>
</label>
<div id="links_landing_container">
<div class="input-group mb-2">
<input type="url" name="link_landing[]" class="form-control" placeholder="https://...">
<button class="btn btn-outline-danger" type="button" onclick="if(document.querySelectorAll('#links_landing_container .input-group').length > 1) this.parentElement.remove()"><i class="fas fa-times"></i></button>
</div>
</div>
</div>
<div class="col-md-12">
<label class="form-label d-flex justify-content-between">
Link de Flyer
<button type="button" class="btn btn-sm btn-link p-0 text-decoration-none" onclick="addLinkInput('links_flyer_container', 'link_flyer[]')">
<i class="fas fa-plus-circle"></i> Añadir otro
</button>
</label>
<div id="links_flyer_container">
<div class="input-group mb-2">
<input type="url" name="link_flyer[]" class="form-control" placeholder="https://...">
<button class="btn btn-outline-danger" type="button" onclick="if(document.querySelectorAll('#links_flyer_container .input-group').length > 1) this.parentElement.remove()"><i class="fas fa-times"></i></button>
</div>
</div>
</div>
<div class="col-12">
<label class="form-label">Instrucciones Adicionales</label>
<textarea name="instrucciones" class="form-control" rows="3"></textarea>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancelar</button>
<button type="submit" class="btn btn-primary">Crear Pedido</button>
</div>
</form>
</div>
</div>
</div>
<!-- Modal Gestionar Video -->
<div class="modal fade" id="gestionarVideoModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<form action="save_marketing_video.php" method="POST">
<input type="hidden" name="action" value="update">
<input type="hidden" name="id" id="edit_id">
<div class="modal-header">
<h5 class="modal-title">Gestionar Pedido</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="row g-3">
<div class="col-md-4">
<label class="form-label">Estado</label>
<select name="estado" id="edit_estado" class="form-select">
<option value="PENDIENTE">PENDIENTE</option>
<option value="EN PROCESO">EN PROCESO</option>
<option value="TERMINADO">TERMINADO</option>
<option value="OBSERVADO">OBSERVADO</option>
<option value="APROBADO">APROBADO</option>
<option value="PUBLICADO">PUBLICADO</option>
</select>
</div>
<div class="col-md-4">
<label class="form-label">Orden</label>
<input type="number" name="orden" id="edit_orden" class="form-control">
</div>
<div class="col-md-4">
<label class="form-label">Fecha Entrega</label>
<input type="date" name="fecha_entrega" id="edit_fecha_entrega" class="form-control">
</div>
<div class="col-md-6">
<label class="form-label">Material</label>
<input type="text" name="material" id="edit_material" class="form-control">
</div>
<div class="col-md-6">
<label class="form-label d-flex justify-content-between">
Ángulo del Video
<button type="button" class="btn btn-sm btn-link p-0 text-decoration-none" onclick="addTextInput('edit_angulo_video_container', 'angulo_video[]', 'Ej: Cenital, 45 grados...')">
<i class="fas fa-plus-circle"></i> Añadir otro
</button>
</label>
<div id="edit_angulo_video_container">
<!-- Se llenará dinámicamente -->
</div>
</div>
<div class="col-md-6">
<label class="form-label d-flex justify-content-between">
Link Inspiración Landing
<button type="button" class="btn btn-sm btn-link p-0 text-decoration-none" onclick="addLinkInput('edit_links_insp_landing_container', 'link_inspiracion_landing[]')">
<i class="fas fa-plus-circle"></i> Añadir otro
</button>
</label>
<div id="edit_links_insp_landing_container">
<!-- Se llenará dinámicamente -->
</div>
</div>
<div class="col-md-6">
<label class="form-label d-flex justify-content-between">
Video Referencial
<button type="button" class="btn btn-sm btn-link p-0 text-decoration-none" onclick="addLinkInput('edit_links_video_container', 'link_inspiracion_video[]')">
<i class="fas fa-plus-circle"></i> Añadir otro
</button>
</label>
<div id="edit_links_video_container">
<!-- Se llenará dinámicamente -->
</div>
</div>
<div class="col-md-6">
<label class="form-label d-flex justify-content-between">
Link Video Final
<button type="button" class="btn btn-sm btn-link p-0 text-decoration-none" onclick="addLinkInput('edit_links_video_final_container', 'link_video[]')">
<i class="fas fa-plus-circle"></i> Añadir otro
</button>
</label>
<div id="edit_links_video_final_container">
<!-- Se llenará dinámicamente -->
</div>
</div>
<div class="col-md-6">
<label class="form-label d-flex justify-content-between">
Link de la Landing
<button type="button" class="btn btn-sm btn-link p-0 text-decoration-none" onclick="addLinkInput('edit_links_landing_container', 'link_landing[]')">
<i class="fas fa-plus-circle"></i> Añadir otro
</button>
</label>
<div id="edit_links_landing_container">
<!-- Se llenará dinámicamente -->
</div>
</div>
<div class="col-md-12">
<label class="form-label d-flex justify-content-between">
Link de Flyer
<button type="button" class="btn btn-sm btn-link p-0 text-decoration-none" onclick="addLinkInput('edit_links_flyer_container', 'link_flyer[]')">
<i class="fas fa-plus-circle"></i> Añadir otro
</button>
</label>
<div id="edit_links_flyer_container">
<!-- Se llenará dinámicamente -->
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger me-auto" onclick="if(confirm('¿Eliminar este pedido?')) window.location.href='delete_marketing_video.php?id=' + document.getElementById('edit_id').value">
<i class="fas fa-trash-alt"></i> Eliminar
</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cerrar</button>
<button type="submit" class="btn btn-success">Guardar Cambios</button>
</div>
</form>
</div>
</div>
</div>
<script>
function switchView(view) {
const tableView = document.getElementById('tableView');
const cardsView = document.getElementById('cardsView');
const btnTable = document.getElementById('btnViewTable');
const btnCards = document.getElementById('btnViewCards');
if (view === 'table') {
tableView.style.display = 'block';
cardsView.style.display = 'none';
btnTable.classList.add('active');
btnCards.classList.remove('active');
localStorage.setItem('video_production_view', 'table');
} else {
tableView.style.display = 'none';
cardsView.style.display = 'block';
btnTable.classList.remove('active');
btnCards.classList.add('active');
localStorage.setItem('video_production_view', 'cards');
}
}
function getStatusBadgeClass(status) {
switch (status) {
case 'PENDIENTE':
return 'bg-warning text-dark';
case 'EN PROCESO':
return 'bg-info text-white';
case 'TERMINADO':
return 'bg-primary text-white';
case 'OBSERVADO':
return 'bg-danger text-white';
case 'APROBADO':
return 'bg-success text-white';
case 'PUBLICADO':
return 'bg-dark text-white';
default:
return 'bg-secondary text-white';
}
}
function updateCardStatusBadge(videoId, status) {
document.querySelectorAll(`.video-card-status-badge[data-video-id="${videoId}"]`).forEach(badge => {
badge.className = `badge status-badge video-card-status-badge ${getStatusBadgeClass(status)} shadow-sm`;
badge.textContent = status || '-';
});
}
function updateStatusCounter(status, delta) {
const counters = {
'PENDIENTE': 'status-count-pendiente',
'EN PROCESO': 'status-count-en-proceso',
'TERMINADO': 'status-count-terminado',
'APROBADO': 'status-count-aprobado'
};
const counterId = counters[status];
if (!counterId) {
return;
}
const counter = document.getElementById(counterId);
if (!counter) {
return;
}
const currentValue = parseInt(counter.textContent, 10);
if (Number.isNaN(currentValue)) {
return;
}
counter.textContent = Math.max(0, currentValue + delta);
}
function syncVideoPayload(videoId, field, value) {
document.querySelectorAll(`[data-video-id="${videoId}"][data-video-payload]`).forEach(button => {
try {
const payload = JSON.parse(button.dataset.videoPayload);
payload[field] = value;
button.dataset.videoPayload = JSON.stringify(payload);
} catch (error) {
console.error('No se pudo sincronizar el payload del video.', error);
}
});
}
function gestionarVideoFromButton(button) {
if (!button) {
return;
}
const rawPayload = button.getAttribute('data-video-payload');
if (!rawPayload) {
return;
}
try {
gestionarVideo(JSON.parse(rawPayload));
} catch (error) {
console.error('No se pudo abrir el modal del video.', error);
}
}
document.addEventListener('DOMContentLoaded', function() {
const savedView = localStorage.getItem('video_production_view') || 'table';
switchView(savedView);
const observationInputs = document.querySelectorAll('.observation-input');
observationInputs.forEach(input => {
let lastSavedValue = input.value;
const status = document.getElementById(`observation-status-${input.dataset.id}`);
const setStatus = (message, className = 'text-muted') => {
if (!status) return;
status.className = `small mt-1 observation-status ${className}`;
status.textContent = message;
};
const saveObservation = () => {
const newValue = input.value;
if (newValue === lastSavedValue) {
return;
}
setStatus('Guardando...', 'text-primary');
fetch('update_marketing_video_field.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
id: input.dataset.id,
field: 'observacion',
value: newValue
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
lastSavedValue = newValue;
setStatus('Guardado', 'text-success');
setTimeout(() => {
if (status && status.textContent === 'Guardado') {
setStatus('', 'text-muted');
}
}, 2000);
} else {
setStatus('Error al guardar', 'text-danger');
alert('Error: ' + data.error);
}
})
.catch(() => {
setStatus('Error al guardar', 'text-danger');
});
};
input.addEventListener('blur', saveObservation);
input.addEventListener('keydown', function(e) {
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
input.blur();
}
});
});
const statusSelects = document.querySelectorAll('.status-select');
statusSelects.forEach(select => {
let lastSavedValue = select.value;
const feedback = document.getElementById(`status-feedback-${select.dataset.id}`);
const setFeedback = (message, className = 'text-muted') => {
if (!feedback) return;
feedback.className = `small mt-1 status-feedback ${className}`;
feedback.textContent = message;
};
select.addEventListener('change', function() {
const newValue = select.value;
const previousValue = lastSavedValue;
if (newValue === previousValue) {
return;
}
select.disabled = true;
select.dataset.status = newValue;
setFeedback('Guardando...', 'text-primary');
fetch('update_marketing_video_field.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
id: select.dataset.id,
field: 'estado',
value: newValue
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
lastSavedValue = newValue;
syncVideoPayload(select.dataset.id, 'estado', newValue);
updateCardStatusBadge(select.dataset.id, newValue);
updateStatusCounter(previousValue, -1);
updateStatusCounter(newValue, 1);
setFeedback('Guardado', 'text-success');
setTimeout(() => {
if (feedback && feedback.textContent === 'Guardado') {
setFeedback('', 'text-muted');
}
}, 2000);
} else {
select.value = previousValue;
select.dataset.status = previousValue;
setFeedback('Error al guardar', 'text-danger');
alert('Error: ' + data.error);
}
})
.catch(() => {
select.value = previousValue;
select.dataset.status = previousValue;
setFeedback('Error al guardar', 'text-danger');
})
.finally(() => {
select.disabled = false;
});
});
});
});
function addLinkInput(containerId, inputName, value = '') {
const container = document.getElementById(containerId);
const div = document.createElement('div');
div.className = 'input-group mb-2';
div.innerHTML = `
<input type="url" name="${inputName}" class="form-control" placeholder="https://..." value="${value}">
<button class="btn btn-outline-danger" type="button" onclick="if(document.querySelectorAll('#${containerId} .input-group').length > 1) this.parentElement.remove()"><i class="fas fa-times"></i></button>
`;
container.appendChild(div);
}
function addTextInput(containerId, inputName, placeholder = '', value = '') {
const container = document.getElementById(containerId);
const div = document.createElement('div');
div.className = 'input-group mb-2';
div.innerHTML = `
<input type="text" name="${inputName}" class="form-control" placeholder="${placeholder}" value="${value}">
<button class="btn btn-outline-danger" type="button" onclick="if(document.querySelectorAll('#${containerId} .input-group').length > 1) this.parentElement.remove()"><i class="fas fa-times"></i></button>
`;
container.appendChild(div);
}
function gestionarVideo(video) {
document.getElementById('edit_id').value = video.id;
document.getElementById('edit_estado').value = video.estado;
document.getElementById('edit_orden').value = video.orden || 0;
document.getElementById('edit_fecha_entrega').value = video.fecha_entrega || '';
document.getElementById('edit_material').value = video.material || '';
// Llenar links dinámicos
const fillLinks = (containerId, inputName, data) => {
const container = document.getElementById(containerId);
container.innerHTML = '';
if (data) {
const links = data.split(',');
links.forEach(link => {
if (link.trim()) addLinkInput(containerId, inputName, link.trim());
});
}
if (container.innerHTML === '') {
addLinkInput(containerId, inputName);
}
};
const fillTexts = (containerId, inputName, data, placeholder = '') => {
const container = document.getElementById(containerId);
container.innerHTML = '';
if (data) {
const texts = data.split(',');
texts.forEach(text => {
if (text.trim()) addTextInput(containerId, inputName, placeholder, text.trim());
});
}
if (container.innerHTML === '') {
addTextInput(containerId, inputName, placeholder);
}
};
fillTexts('edit_angulo_video_container', 'angulo_video[]', video.angulo_video, 'Ej: Cenital, 45 grados...');
fillLinks('edit_links_insp_landing_container', 'link_inspiracion_landing[]', video.link_inspiracion_landing);
fillLinks('edit_links_video_container', 'link_inspiracion_video[]', video.link_inspiracion_video);
fillLinks('edit_links_video_final_container', 'link_video[]', video.link_video);
fillLinks('edit_links_landing_container', 'link_landing[]', video.link_landing);
fillLinks('edit_links_flyer_container', 'link_flyer[]', video.link_flyer);
var myModal = new bootstrap.Modal(document.getElementById('gestionarVideoModal'));
myModal.show();
}
function eliminarVideo(id) {
if (confirm('¿Estás seguro de que deseas eliminar este pedido de video? Esta acción no se puede deshacer.')) {
window.location.href = 'delete_marketing_video.php?id=' + id;
}
}
document.addEventListener('DOMContentLoaded', function() {
const editableCells = document.querySelectorAll('.editable');
editableCells.forEach(cell => {
cell.addEventListener('dblclick', function(e) {
if (this.querySelector('input') || this.querySelector('select')) return;
// Evitar que el doble clic seleccione el texto
window.getSelection().removeAllRanges();
const id = this.getAttribute('data-id');
const field = this.getAttribute('data-field');
// Usar data-value si existe (para material y angulo_video), de lo contrario innerText
let currentValue = this.getAttribute('data-value');
if (currentValue === null) {
currentValue = this.innerText.trim() === '-' ? '' : this.innerText.trim();
}
let input;
if (field === 'estado') {
input = document.createElement('select');
input.className = 'form-select form-select-sm inline-edit-input';
const options = ['PENDIENTE', 'EN PROCESO', 'TERMINADO', 'OBSERVADO', 'APROBADO', 'PUBLICADO'];
options.forEach(opt => {
const option = document.createElement('option');
option.value = opt;
option.text = opt;
if (opt === currentValue) option.selected = true;
input.appendChild(option);
});
} else {
input = document.createElement('input');
input.type = 'text';
input.className = 'form-control form-control-sm inline-edit-input';
input.value = currentValue;
}
const originalContent = this.innerHTML;
this.innerHTML = '';
this.appendChild(input);
input.focus();
const saveChange = () => {
const newValue = input.value;
if (newValue === currentValue) {
this.innerHTML = originalContent;
return;
}
fetch('update_marketing_video_field.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ id, field, value: newValue })
})
.then(response => response.json())
.then(data => {
if (data.success) {
// Actualizar data-value para la próxima edición
this.setAttribute('data-value', newValue);
syncVideoPayload(id, field, newValue);
if (field === 'estado') {
let badgeClass = 'bg-secondary';
if (newValue === 'PENDIENTE') badgeClass = 'bg-warning text-dark';
else if (newValue === 'EN PROCESO') badgeClass = 'bg-info';
else if (newValue === 'TERMINADO') badgeClass = 'bg-primary';
else if (newValue === 'OBSERVADO') badgeClass = 'bg-danger';
else if (newValue === 'APROBADO') badgeClass = 'bg-success';
else if (newValue === 'PUBLICADO') badgeClass = 'bg-dark';
this.innerHTML = `<span class="badge status-badge ${badgeClass}">${newValue}</span>`;
} else if (field === 'material' || field === 'angulo_video') {
const items = newValue.split(',').map(i => i.trim()).filter(i => i !== '');
if (items.length === 0) {
this.innerHTML = '<div class="tag-container"><span class="text-muted">-</span></div>';
} else {
let html = '<div class="tag-container">';
const tagClass = field === 'material' ? 'tag-item-material' : 'tag-item-angulo';
items.forEach(item => {
html += `<span class="tag-item ${tagClass}">${item}</span>`;
});
html += '</div>';
this.innerHTML = html;
}
} else {
this.innerHTML = `<div>${newValue || '-'}</div>`;
}
} else {
alert('Error: ' + data.error);
this.innerHTML = originalContent;
}
})
.catch(error => {
console.error('Error:', error);
this.innerHTML = originalContent;
});
};
input.addEventListener('blur', saveChange);
input.addEventListener('keydown', function(e) {
if (e.key === 'Enter') {
input.blur();
} else if (e.key === 'Escape') {
cell.innerHTML = originalContent;
}
});
});
});
});
</script>
<?php include 'layout_footer.php'; ?>