Autosave: 20260519-012209

This commit is contained in:
Flatlogic Bot 2026-05-19 01:22:09 +00:00
parent a8d2f9cb0a
commit 26b4617b8a
5 changed files with 212 additions and 38 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

View File

@ -85,10 +85,29 @@ $costos = $stmt->fetchAll(PDO::FETCH_ASSOC);
margin-bottom: 2px;
}
.img-v3 {
width: 40px;
height: 40px;
width: 120px;
height: 120px;
object-fit: cover;
border-radius: 6px;
border-radius: 12px;
cursor: pointer;
transition: all 0.4s cubic-bezier(0.165, 0.84, 0.44, 1);
box-shadow: 0 4px 12px rgba(0,0,0,0.12);
}
.img-v3:hover {
transform: scale(2.2);
z-index: 100;
position: relative;
box-shadow: 0 15px 30px rgba(0,0,0,0.2);
}
.img-placeholder {
width: 120px;
height: 120px;
cursor: pointer;
transition: background-color 0.2s;
border-radius: 12px;
}
.img-placeholder:hover {
background-color: #e2e6ea !important;
}
.btn-action {
width: 28px;
@ -108,16 +127,16 @@ $costos = $stmt->fetchAll(PDO::FETCH_ASSOC);
}
</style>
<div class="d-flex justify-content-between align-items-center mb-4 mt-2">
<div>
<p class="text-muted mb-0">Versión V3: Tabla limpia e independiente. Los cambios se guardan automáticamente.</p>
<div class="row align-items-center mb-4 mt-2">
<div class="col-md-6">
<p class="text-muted mb-0">Total de registros encontrados: <span class="fw-bold text-dark"><?php echo count($costos); ?></span></p>
</div>
<div class="d-flex gap-2">
<button type="button" class="btn btn-outline-secondary" onclick="location.reload()">
<div class="col-md-6 text-end">
<button type="button" class="btn btn-outline-secondary me-2" onclick="location.reload()">
<i class="fas fa-sync-alt"></i> Actualizar
</button>
<button type="button" class="btn btn-primary shadow-sm" data-bs-toggle="modal" data-bs-target="#modalNuevoV3">
<i class="fas fa-plus me-2"></i> Nuevo Producto
<button type="button" id="btnAbrirModalV3" class="btn btn-success btn-lg shadow-sm px-4">
<i class="fas fa-plus-circle me-2"></i> CREAR NUEVO PRODUCTO
</button>
</div>
</div>
@ -129,6 +148,13 @@ $costos = $stmt->fetchAll(PDO::FETCH_ASSOC);
</div>
<?php endif; ?>
<?php if (isset($_GET['error'])): ?>
<div class="alert alert-danger alert-dismissible fade show shadow-sm" role="alert">
<i class="fas fa-exclamation-triangle me-2"></i> <?php echo htmlspecialchars($_GET['error']); ?>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<?php endif; ?>
<div class="card shadow-sm border-0">
<div class="card-body p-0">
<div class="table-responsive" style="max-height: 70vh;">
@ -162,6 +188,15 @@ $costos = $stmt->fetchAll(PDO::FETCH_ASSOC);
</td>
</tr>
<?php endif; ?>
<?php
if (!function_exists('getValV3')) {
function getValV3($promo) {
if (empty($promo)) return 0;
preg_match('/[\d.]+/', $promo, $matches);
return isset($matches[0]) ? floatval($matches[0]) : 0;
}
}
?>
<?php foreach ($costos as $c): ?>
<?php
$costo_prod = ($c['costo_producto'] ?? 0);
@ -172,15 +207,9 @@ $costos = $stmt->fetchAll(PDO::FETCH_ASSOC);
$costo_delivery_p = ($c['delivery_provincia'] ?? 0);
$costo_publicidad = ($c['costo_publicitario'] ?? 0);
function getVal($promo) {
if (empty($promo)) return 0;
preg_match('/[\d.]+/', $promo, $matches);
return isset($matches[0]) ? floatval($matches[0]) : 0;
}
$p1 = getVal($c['promo_1']);
$p2 = getVal($c['promo_2']);
$p3 = getVal($c['promo_3']);
$p1 = getValV3($c['promo_1']);
$p2 = getValV3($c['promo_2']);
$p3 = getValV3($c['promo_3']);
// Cálculos Local
$r1_l = $p1 > 0 ? $p1 - ($costo_prod + $costo_film + $costo_asesora + $costo_delivery + $costo_publicidad) : null;
@ -200,13 +229,16 @@ $costos = $stmt->fetchAll(PDO::FETCH_ASSOC);
</div>
</td>
<td class="text-center">
<?php if ($c['foto_producto']): ?>
<img src="<?php echo $c['foto_producto']; ?>" class="img-v3">
<?php else: ?>
<div class="bg-light rounded d-flex align-items-center justify-content-center" style="width:40px; height:40px;">
<i class="fas fa-image text-muted"></i>
</div>
<?php endif; ?>
<div class="position-relative d-inline-block">
<?php if ($c['foto_producto']): ?>
<img src="<?php echo $c['foto_producto']; ?>" class="img-v3" onclick="triggerImageUpload(<?php echo $c['id']; ?>)">
<?php else: ?>
<div class="bg-light rounded d-flex align-items-center justify-content-center img-placeholder" onclick="triggerImageUpload(<?php echo $c['id']; ?>)">
<i class="fas fa-camera text-muted"></i>
</div>
<?php endif; ?>
<input type="file" id="file-input-<?php echo $c['id']; ?>" class="d-none" accept="image/*" onchange="uploadImage(<?php echo $c['id']; ?>)">
</div>
</td>
<td><input type="number" step="0.01" class="input-cell cost-input" data-field="costo_producto" value="<?php echo $c['costo_producto']; ?>"></td>
<td><input type="number" step="0.01" class="input-cell cost-input" data-field="costo_fijo_film" value="<?php echo $c['costo_fijo_film']; ?>"></td>
@ -278,12 +310,13 @@ $costos = $stmt->fetchAll(PDO::FETCH_ASSOC);
<div class="modal-body p-4">
<div class="mb-3">
<label class="form-label fw-bold">Seleccionar Producto</label>
<select name="producto_id" class="form-select form-select-lg" required onchange="fetchProductCost(this.value)">
<option value="">Seleccionar...</option>
<select name="producto_id" class="form-select form-select-lg" onchange="fetchProductCost(this.value)">
<option value="">General / Sin producto específico</option>
<?php foreach ($productos_select as $p): ?>
<option value="<?php echo $p['id']; ?>"><?php echo htmlspecialchars($p['nombre']); ?></option>
<?php endforeach; ?>
</select>
<small class="text-muted">Si no seleccionas uno, se marcará como "General".</small>
</div>
<div class="row">
<div class="col-md-6 mb-3">
@ -389,10 +422,79 @@ function recalculateRowVisual(row) {
}
function eliminarRow(id) {
if (confirm('¿Eliminar este registro de la V3?')) {
window.location.href = 'delete_marketing_video_v3.php?id=' + id;
if (confirm("¿Eliminar este registro de la V3?")) {
window.location.href = "delete_marketing_video_v3.php?id=" + id;
}
}
function triggerImageUpload(id) {
document.getElementById(`file-input-${id}`).click();
}
function uploadImage(id) {
const fileInput = document.getElementById(`file-input-${id}`);
if (!fileInput.files || !fileInput.files[0]) return;
const formData = new FormData();
formData.append('id', id);
formData.append('foto_producto', fileInput.files[0]);
const cell = fileInput.closest('td');
const originalContent = cell.innerHTML;
cell.innerHTML = '<div class="spinner-border spinner-border-sm text-primary" role="status"></div>';
fetch('update_marketing_video_image_v3.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
location.reload(); // Recargamos para ver la nueva imagen
} else {
alert('Error al subir imagen: ' + data.error);
cell.innerHTML = originalContent;
}
})
.catch(err => {
console.error(err);
alert('Error técnico al subir imagen');
cell.innerHTML = originalContent;
});
}
// Script de apertura forzada y diagnóstico
document.addEventListener('DOMContentLoaded', function() {
const btn = document.getElementById('btnAbrirModalV3');
const modalEl = document.getElementById('modalNuevoV3');
if (btn && modalEl) {
btn.addEventListener('click', function() {
console.log('Intento de apertura manual del modal...');
try {
// Intento 1: Usando la API de Bootstrap 5
if (typeof bootstrap !== 'undefined') {
const modalInstance = new bootstrap.Modal(modalEl);
modalInstance.show();
console.log('Modal abierto con bootstrap.Modal');
} else {
// Intento 2: Si bootstrap no está definido, intentar vía jQuery si existe
if (typeof $ !== 'undefined' && typeof $.fn.modal !== 'undefined') {
$(modalEl).modal('show');
console.log('Modal abierto con jQuery');
} else {
alert('Error: No se encontró la librería de Bootstrap. Por favor, recarga la página.');
}
}
} catch (err) {
console.error('Error al abrir modal:', err);
alert('Hubo un problema técnico al abrir la ventana. Error: ' + err.message);
}
});
} else {
console.error('No se encontró el botón o el modal en el DOM');
}
});
</script>
<?php include 'layout_footer.php'; ?>

View File

@ -1,9 +1,11 @@
<?php
session_start();
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
include 'db/config.php';
if (!isset($_SESSION['user_id']) || !in_array($_SESSION['user_role'], ['Administrador', 'admin'])) {
header('Location: login.php');
if (!isset($_SESSION['user_role']) || !in_array($_SESSION['user_role'], ['Administrador', 'admin'])) {
header('Location: dashboard.php');
exit();
}

View File

@ -1,9 +1,11 @@
<?php
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
include 'db/config.php';
session_start();
if (!isset($_SESSION['user_id']) || !in_array($_SESSION['user_role'], ['Administrador', 'admin'])) {
header('Location: login.php');
if (!isset($_SESSION['user_role']) || !in_array($_SESSION['user_role'], ['Administrador', 'admin'])) {
header('Location: dashboard.php');
exit();
}
@ -31,6 +33,8 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST') {
}
try {
$db->beginTransaction();
$stmt = $db->prepare("INSERT INTO marketing_videos_v3 (
producto_id,
foto_producto,
@ -45,14 +49,29 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST') {
]);
$video_id = $db->lastInsertId();
if (!$video_id) {
throw new Exception("No se pudo obtener el ID del video insertado.");
}
// Guardar costo inicial en la tabla de costos V3
$stmt_costo = $db->prepare("INSERT INTO marketing_costos_v3 (video_id, costo_producto, inversion_total) VALUES (?, ?, ?)");
$stmt_costo->execute([$video_id, $costo_producto, $costo_producto]);
$db->commit();
$redirect = $_POST['redirect'] ?? 'calculo_costos_v3.php?success=1';
header('Location: ' . $redirect);
} catch (PDOException $e) {
echo "Error: " . $e->getMessage();
exit();
} catch (Exception $e) {
if ($db->inTransaction()) {
$db->rollBack();
}
error_log("Error en save_marketing_video_v3.php: " . $e->getMessage());
header('Location: calculo_costos_v3.php?error=' . urlencode($e->getMessage()));
exit();
}
} else {
header('Location: calculo_costos_v3.php');
exit();
}
?>

View File

@ -0,0 +1,51 @@
<?php
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
include 'db/config.php';
if (!isset($_SESSION['user_role']) || !in_array($_SESSION['user_role'], ['Administrador', 'admin'])) {
echo json_encode(['success' => false, 'error' => 'No autorizado']);
exit();
}
if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_FILES['foto_producto']) && isset($_POST['id'])) {
$db = db();
$id = $_POST['id'];
if ($_FILES['foto_producto']['error'] == 0) {
$target_dir = "assets/uploads/marketing_images/";
if (!is_dir($target_dir)) {
mkdir($target_dir, 0777, true);
}
$file_extension = pathinfo($_FILES["foto_producto"]["name"], PATHINFO_EXTENSION);
$file_name = uniqid() . '_v3.' . $file_extension;
$target_file = $target_dir . $file_name;
if (move_uploaded_file($_FILES["foto_producto"]["tmp_name"], $target_file)) {
try {
// Opcional: borrar imagen anterior si existe
$stmt_old = $db->prepare("SELECT foto_producto FROM marketing_videos_v3 WHERE id = ?");
$stmt_old->execute([$id]);
$old_photo = $stmt_old->fetchColumn();
if ($old_photo && file_exists($old_photo)) {
unlink($old_photo);
}
$stmt = $db->prepare("UPDATE marketing_videos_v3 SET foto_producto = ? WHERE id = ?");
$stmt->execute([$target_file, $id]);
echo json_encode(['success' => true, 'path' => $target_file]);
} catch (Exception $e) {
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
}
} else {
echo json_encode(['success' => false, 'error' => 'Error al mover el archivo']);
}
} else {
echo json_encode(['success' => false, 'error' => 'Error en la subida: ' . $_FILES['foto_producto']['error']]);
}
} else {
echo json_encode(['success' => false, 'error' => 'Solicitud inválida']);
}
?>