Autosave: 20260519-012209
This commit is contained in:
parent
a8d2f9cb0a
commit
26b4617b8a
BIN
assets/uploads/marketing_images/6a0bb9e9a7e9b_v3.webp
Normal file
BIN
assets/uploads/marketing_images/6a0bb9e9a7e9b_v3.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 61 KiB |
@ -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'; ?>
|
||||
@ -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();
|
||||
}
|
||||
|
||||
|
||||
@ -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();
|
||||
}
|
||||
?>
|
||||
51
update_marketing_video_image_v3.php
Normal file
51
update_marketing_video_image_v3.php
Normal 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']);
|
||||
}
|
||||
?>
|
||||
Loading…
x
Reference in New Issue
Block a user