Autosave: 20260203-220138

This commit is contained in:
Flatlogic Bot 2026-02-03 22:01:38 +00:00
parent 5ebd11482d
commit 2e2d50688f
32 changed files with 260 additions and 350 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

View File

@ -1,150 +1,74 @@
<?php
session_start();
if (!isset($_SESSION['user_id'])) {
header('Location: login.php');
exit;
}
$pageTitle = "Configuración del Kanban";
require_once 'layout_header.php';
require_once 'db/config.php';
$db = db();
// Fetch banner text for the form
$stmt_banner = $db->prepare('SELECT valor FROM configuracion WHERE clave = ?');
$stmt_banner->execute(['banner_text']);
$banner_text = $stmt_banner->fetchColumn();
// Asegurarse de que el usuario sea administrador
if ($_SESSION['user_role'] !== 'Administrador' && $_SESSION['user_role'] !== 'admin') {
echo "<div class='alert alert-danger'>Acceso denegado.</div>";
require_once 'layout_footer.php';
exit();
}
// Fetch kanban columns
$kanban_columns = $db->query("SELECT id, nombre FROM kanban_columns ORDER BY id")->fetchAll(PDO::FETCH_ASSOC);
$conn = db();
include 'layout_header.php';
// Obtener la configuración actual
$query = "SELECT * FROM configuracion WHERE id = 1";
$result = $conn->query($query);
$config = $result->fetch(PDO::FETCH_ASSOC);
// Columnas disponibles
$available_columns = [
'new' => 'Nuevos',
'reagendado' => 'Reagendado',
'no_contactado' => 'No Contactado',
'contactado' => 'Contactado',
'agendado' => 'Agendado',
'en_ruta' => 'En Ruta',
'entregado' => 'Entregado',
'no_entregado' => 'No Entregado',
'reprogramado' => 'Reprogramado'
];
$visible_columns = $config ? json_decode($config['kanban_columns'], true) : [];
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$new_visible_columns = $_POST['columns'] ?? [];
$json_columns = json_encode($new_visible_columns);
$update_query = "UPDATE configuracion SET kanban_columns = :kanban_columns WHERE id = 1";
$stmt = $conn->prepare($update_query);
$stmt->bindParam(':kanban_columns', $json_columns);
if ($stmt->execute()) {
echo "<div class='alert alert-success'>Configuración guardada correctamente.</div>";
$visible_columns = $new_visible_columns;
} else {
echo "<div class='alert alert-danger'>Error al guardar la configuración.</div>";
}
}
?>
<div class="container mt-5">
<?php if (isset($_SESSION['success_message'])): ?>
<div class="alert alert-success">
<?php echo $_SESSION['success_message']; unset($_SESSION['success_message']); ?>
</div>
<?php endif; ?>
<?php if (isset($_SESSION['error_message'])): ?>
<div class="alert alert-danger">
<?php echo $_SESSION['error_message']; unset($_SESSION['error_message']); ?>
</div>
<?php endif; ?>
<h2>Configuración de Columnas del Kanban</h2>
<p>Selecciona las columnas que deseas que sean visibles en el tablero Kanban.</p>
<h1 class="mb-4">Configuración General</h1>
<!-- Banner Text Management -->
<div class="card mb-5">
<div class="card-header">
<h2>Editar Banner del Kanban</h2>
</div>
<div class="card-body">
<form action="save_banner_text.php" method="post">
<div class="form-group">
<label for="banner_text">Texto del banner</label>
<textarea name="banner_text" id="banner_text" class="form-control" rows="2"><?php echo htmlspecialchars($banner_text ?? ''); ?></textarea>
<small class="form-text text-muted">Este texto aparecerá en un banner deslizante en la parte superior del tablero Kanban.</small>
<form method="POST">
<div class="row">
<?php foreach ($available_columns as $key => $name): ?>
<div class="col-md-3">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="columns[]" value="<?php echo $key; ?>" id="col_<?php echo $key; ?>" <?php echo in_array($key, $visible_columns) ? 'checked' : ''; ?>>
<label class="form-check-label" for="col_<?php echo $key; ?>">
<?php echo htmlspecialchars($name); ?>
</label>
</div>
</div>
<button type="submit" class="btn btn-primary">Guardar Banner</button>
</form>
<?php endforeach; ?>
</div>
</div>
<!-- Cobertura Banner Management -->
<div class="card mb-5">
<div class="card-header">
<h2>Editar Imagen de Cobertura</h2>
</div>
<div class="card-body">
<div class="text-center mb-3">
<p>Imagen actual:</p>
<?php
$banner_image = 'assets/uploads/cobertura_banner.jpg';
if (file_exists($banner_image)):
?>
<img src="<?php echo $banner_image; ?>?t=<?php echo time(); ?>" alt="Banner de Cobertura" class="img-fluid rounded" style="max-height: 200px;">
<?php else: ?>
<p>No hay ninguna imagen de cobertura cargada.</p>
<?php endif; ?>
</div>
<form action="save_cobertura_banner.php" method="post" enctype="multipart/form-data">
<div class="form-group">
<label for="banner_image">Subir o reemplazar imagen</label>
<input type="file" class="form-control-file" id="banner_image" name="banner_image" accept="image/jpeg,image/png,image/gif,image/webp" required>
<small class="form-text text-muted">La imagen se guardará como .jpg y se mostrará en la sección de Cobertura.</small>
</div>
<button type="submit" class="btn btn-primary">Guardar Imagen</button>
</form>
</div>
</div>
<!-- Cobertura Xpress Banner Management -->
<div class="card mb-5">
<div class="card-header">
<h2>Editar Imagen de Cobertura Xpress</h2>
</div>
<div class="card-body">
<div class="text-center mb-3">
<p>Imagen actual:</p>
<?php
$banner_image_xpress = 'assets/uploads/cobertura_xpress_banner.jpg';
if (file_exists($banner_image_xpress)):
?>
<img src="<?php echo $banner_image_xpress; ?>?t=<?php echo time(); ?>" alt="Banner de Cobertura Xpress" class="img-fluid rounded" style="max-height: 200px;">
<?php else: ?>
<p>No hay ninguna imagen de Cobertura Xpress cargada.</p>
<?php endif; ?>
</div>
<form action="save_cobertura_xpress_banner.php" method="post" enctype="multipart/form-data">
<div class="form-group">
<label for="banner_image_xpress">Subir o reemplazar imagen</label>
<input type="file" class="form-control-file" id="banner_image_xpress" name="banner_image_xpress" accept="image/jpeg,image/png,image/gif,image/webp" required>
<small class="form-text text-muted">Esta imagen se mostrará en la sección de Cobertura Xpress.</small>
</div>
<button type="submit" class="btn btn-primary">Guardar Imagen</button>
</form>
</div>
</div>
<!-- Column Management -->
<div class="card">
<div class="card-header">
<h2>Configuración de Columnas Kanban</h2>
</div>
<div class="card-body">
<table class="table table-bordered">
<thead>
<tr>
<th>ID</th>
<th>Nombre de la Columna</th>
<th>Acción</th>
</tr>
</thead>
<tbody>
<?php foreach ($kanban_columns as $column): ?>
<tr>
<form action="edit_column.php" method="post">
<td><?php echo htmlspecialchars($column['id']); ?></td>
<td>
<input type="text" name="nombre" class="form-control" value="<?php echo htmlspecialchars($column['nombre']); ?>">
<input type="hidden" name="id" value="<?php echo $column['id']; ?>">
</td>
<td>
<button type="submit" class="btn btn-primary btn-sm">Guardar</button>
<a href="delete_column.php?id=<?php echo $column['id']; ?>" class="btn btn-danger btn-sm" onclick="return confirm('¿Estás seguro?')">Eliminar</a>
</td>
</form>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<form action="add_column.php" method="post" class="form-inline mt-3">
<div class="form-group">
<input type="text" name="nombre" class="form-control" placeholder="Nueva Columna" required>
</div>
<button type="submit" class="btn btn-success ml-2">Agregar Columna</button>
</form>
</div>
</div>
<button type="submit" class="btn btn-primary mt-3">Guardar Configuración</button>
</form>
</div>
<?php include 'layout_footer.php'; ?>
<?php require_once 'layout_footer.php'; ?>

View File

@ -0,0 +1 @@
ALTER TABLE `flujo_caja` ADD `c_pedidos` VARCHAR(255) NULL AFTER `fecha`;

View File

@ -9,12 +9,21 @@ $start_date = "$year-$month-01";
$days_in_month = cal_days_in_month(CAL_GREGORIAN, $month, $year);
$end_date = "$year-$month-$days_in_month";
$columns = [
// Columns from DB that are editable
$db_columns = [
'bcp_yape', 'b_nacion', 'interbank', 'bbva', 'otros_ingresos',
'tu1', 'tu2', 'tu3', 'fl1', 'fl2', 'fl3',
'rc_envio', 'rc_contraent', 'sc'
'rc_contraent', 'sc', 'c_pedidos'
];
// Columns to display in the table body in order
$display_columns = [
'bcp_yape', 'b_nacion', 'interbank', 'bbva', 'otros_ingresos',
'tu1', 'tu2', 'tu3', 'fl1', 'fl2', 'fl3',
'rc_envio', 'rc_contraent'
];
// Fetch data from DB
$flujo_data = [];
try {
@ -33,26 +42,39 @@ try {
$all_days_data = [];
for ($day = 1; $day <= $days_in_month; $day++) {
$date = date('Y-m-d', strtotime("$year-$month-$day"));
$all_days_data[$date] = isset($flujo_data[$date]) ? $flujo_data[$date] : array_fill_keys($columns, '0.00');
if (isset($flujo_data[$date])) {
$all_days_data[$date] = $flujo_data[$date];
} else {
// Initialize with 0.00 for all expected columns
$all_days_data[$date] = array_fill_keys($db_columns, '0.00');
}
}
// Calculate totals
$totals = array_fill_keys($columns, 0);
$totals = array_fill_keys($display_columns, 0);
$totals['sc'] = 0;
$totals['c_pedidos'] = 0;
$totals['total_ingresos'] = 0;
$totals['total_inversion'] = 0;
$totals['recaudo_final'] = 0;
foreach ($all_days_data as $day_data) {
$ingresos_dia = (float)$day_data['bcp_yape'] + (float)$day_data['b_nacion'] + (float)$day_data['interbank'] + (float)$day_data['bbva'] + (float)$day_data['otros_ingresos'] + (float)$day_data['rc_envio'] + (float)$day_data['rc_contraent'];
$inversion_dia = (float)$day_data['tu1'] + (float)$day_data['tu2'] + (float)$day_data['tu3'] + (float)$day_data['fl1'] + (float)$day_data['fl2'] + (float)$day_data['fl3'];
foreach ($all_days_data as $date => &$day_data) {
$rc_envio_dia = (float)($day_data['bcp_yape'] ?? 0) + (float)($day_data['b_nacion'] ?? 0) + (float)($day_data['interbank'] ?? 0) + (float)($day_data['bbva'] ?? 0) + (float)($day_data['otros_ingresos'] ?? 0);
$day_data['rc_envio'] = $rc_envio_dia;
$ingresos_dia = $rc_envio_dia + (float)($day_data['rc_contraent'] ?? 0);
$inversion_dia = (float)($day_data['tu1'] ?? 0) + (float)($day_data['tu2'] ?? 0) + (float)($day_data['tu3'] ?? 0) + (float)($day_data['fl1'] ?? 0) + (float)($day_data['fl2'] ?? 0) + (float)($day_data['fl3'] ?? 0);
foreach ($columns as $col) {
$totals[$col] += (float)$day_data[$col];
foreach ($display_columns as $col) {
$totals[$col] += (float)($day_data[$col] ?? 0);
}
$totals['sc'] += (float)($day_data['sc'] ?? 0);
$totals['c_pedidos'] += (int)($day_data['c_pedidos'] ?? 0);
$totals['total_ingresos'] += $ingresos_dia;
$totals['total_inversion'] += $inversion_dia;
$totals['recaudo_final'] += ($ingresos_dia - $inversion_dia);
}
unset($day_data);
?>
@ -108,14 +130,15 @@ foreach ($all_days_data as $day_data) {
<thead>
<tr>
<th rowspan="2" style="vertical-align: middle; text-align: center;">Fecha</th>
<th rowspan="2" style="vertical-align: middle; text-align: center;">C.pedidos</th>
<th colspan="5" style="text-align: center;">Ingresos</th>
<th colspan="6" style="text-align: center;">Inversion Publicitaria</th>
<th rowspan="2" style="vertical-align: middle; text-align: center;">RC ENVIO</th>
<th rowspan="2" style="vertical-align: middle; text-align: center;">RC CONTRAENT</th>
<th rowspan="2" style="vertical-align: middle; text-align: center;">Sc</th>
<th rowspan="2" style="vertical-align: middle; text-align: center;">Total Ingresos</th>
<th rowspan="2" style="vertical-align: middle; text-align: center;">Total Inversion Publicitaria</th>
<th rowspan="2" style="vertical-align: middle; text-align: center;">Recaudo final</th>
<th rowspan="2" style="vertical-align: middle; text-align: center;">Sc</th>
</tr>
<tr>
<th>BCP/YAPE</th>
@ -135,24 +158,32 @@ foreach ($all_days_data as $day_data) {
<?php foreach ($all_days_data as $date => $day_data): ?>
<tr data-date="<?php echo $date; ?>">
<td><?php echo $date; ?></td>
<?php foreach ($columns as $col): ?>
<td contenteditable="true" data-column="<?php echo $col; ?>"><?php echo htmlspecialchars(number_format((float)$day_data[$col], 2, '.', '')); ?></td>
<td contenteditable="true" data-column="c_pedidos"><?php echo htmlspecialchars(number_format((int)($day_data['c_pedidos'] ?? 0), 0, '', '')); ?></td>
<?php foreach ($display_columns as $col): ?>
<?php if ($col === 'rc_envio'): ?>
<td data-column="rc_envio"><?php echo htmlspecialchars(number_format((float)($day_data[$col] ?? 0), 2, '.', '')); ?></td>
<?php else: ?>
<td contenteditable="true" data-column="<?php echo $col; ?>"><?php echo htmlspecialchars(number_format((float)($day_data[$col] ?? 0), 2, '.', '')); ?></td>
<?php endif; ?>
<?php endforeach; ?>
<td class="total-ingresos">0.00</td>
<td class="total-inversion">0.00</td>
<td class="recaudo-final">0.00</td>
<td contenteditable="true" data-column="sc"><?php echo htmlspecialchars(number_format((float)($day_data['sc'] ?? 0), 2, '.', '')); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
<tfoot>
<tr>
<th>TOTAL</th>
<?php foreach ($columns as $col): ?>
<th data-total-column="c_pedidos"><?php echo number_format($totals['c_pedidos'], 0); ?></th>
<?php foreach ($display_columns as $col): ?>
<th data-total-column="<?php echo $col; ?>"><?php echo number_format($totals[$col], 2); ?></th>
<?php endforeach; ?>
<th data-total-column="total-ingresos"><?php echo number_format($totals['total_ingresos'], 2); ?></th>
<th data-total-column="total-inversion"><?php echo number_format($totals['total_inversion'], 2); ?></th>
<th data-total-column="recaudo-final"><?php echo number_format($totals['recaudo_final'], 2); ?></th>
<th data-total-column="sc"><?php echo number_format($totals['sc'], 2); ?></th>
</tr>
</tfoot>
</table>
@ -169,7 +200,13 @@ document.addEventListener('DOMContentLoaded', function() {
return parseFloat(cell.textContent.replace(/,/g, '')) || 0;
};
const ingresos = getVal('bcp_yape') + getVal('b_nacion') + getVal('interbank') + getVal('bbva') + getVal('otros_ingresos') + getVal('rc_envio') + getVal('rc_contraent');
const rcEnvio = getVal('bcp_yape') + getVal('b_nacion') + getVal('interbank') + getVal('bbva') + getVal('otros_ingresos');
const rcEnvioCell = row.querySelector('[data-column="rc_envio"]');
if (rcEnvioCell) {
rcEnvioCell.textContent = rcEnvio.toFixed(2);
}
const ingresos = rcEnvio + getVal('rc_contraent');
const totalInversion = getVal('tu1') + getVal('tu2') + getVal('tu3') + getVal('fl1') + getVal('fl2') + getVal('fl3');
const recaudoFinal = ingresos - totalInversion;
@ -180,16 +217,20 @@ document.addEventListener('DOMContentLoaded', function() {
function updateGrandTotals() {
const grandTotals = {
<?php foreach ($columns as $col) echo "'$col': 0,"; ?>
<?php foreach ($display_columns as $col) echo "'$col': 0,"; ?>
'c_pedidos': 0,
'sc': 0,
'total-ingresos': 0,
'total-inversion': 0,
'recaudo-final': 0
};
table.querySelectorAll('tbody tr').forEach(row => {
<?php foreach ($columns as $col): ?>
<?php foreach ($display_columns as $col): ?>
grandTotals['<?php echo $col; ?>'] += parseFloat(row.querySelector(`[data-column="<?php echo $col; ?>"]`).textContent.replace(/,/g, '')) || 0;
<?php endforeach; ?>
grandTotals['c_pedidos'] += parseInt(row.querySelector(`[data-column="c_pedidos"]`).textContent.replace(/,/g, '')) || 0;
grandTotals['sc'] += parseFloat(row.querySelector(`[data-column="sc"]`).textContent.replace(/,/g, '')) || 0;
grandTotals['total-ingresos'] += parseFloat(row.querySelector('.total-ingresos').textContent.replace(/,/g, '')) || 0;
grandTotals['total-inversion'] += parseFloat(row.querySelector('.total-inversion').textContent.replace(/,/g, '')) || 0;
grandTotals['recaudo-final'] += parseFloat(row.querySelector('.recaudo-final').textContent.replace(/,/g, '')) || 0;
@ -198,7 +239,11 @@ document.addEventListener('DOMContentLoaded', function() {
for (const key in grandTotals) {
const th = table.querySelector(`tfoot [data-total-column="${key}"]`);
if (th) {
th.textContent = grandTotals[key].toFixed(2);
if (key === 'c_pedidos') {
th.textContent = grandTotals[key];
} else {
th.textContent = grandTotals[key].toFixed(2);
}
}
}
}
@ -222,12 +267,23 @@ document.addEventListener('DOMContentLoaded', function() {
const row = cell.closest('tr');
const date = row.dataset.date;
const column = cell.dataset.column;
let value = parseFloat(cell.textContent.replace(/,/g, ''));
let value;
if (column === 'c_pedidos') {
value = parseInt(cell.textContent.replace(/,/g, ''));
} else {
value = parseFloat(cell.textContent.replace(/,/g, ''));
}
if (isNaN(value)) {
value = 0;
}
cell.textContent = value.toFixed(2);
if (column === 'c_pedidos') {
cell.textContent = value;
} else {
cell.textContent = value.toFixed(2);
}
fetch('save_flujo_caja.php', {
method: 'POST',
@ -258,4 +314,4 @@ document.addEventListener('DOMContentLoaded', function() {
<?php
require_once 'layout_footer.php';
?>
?>

View File

@ -1,223 +1,146 @@
<?php
session_start();
require_once 'db/config.php';
require_once 'layout_header.php';
if (!isset($_SESSION['username'])) {
header("Location: login.php");
exit();
}
$username = $_SESSION['username'];
$role = $_SESSION['role'];
// Conectar a la base de datos
$pdo = db();
// Fetch banner text
$stmt_banner = $pdo->prepare('SELECT valor FROM configuracion WHERE clave = ?');
$stmt_banner->execute(['banner_text']);
$banner_text = $stmt_banner->fetchColumn();
if (empty($banner_text)) {
$banner_text = '¡Define tu frase motivacional en la sección de Información de Producto!';
}
// Obtener la configuración de las columnas del Kanban
$stmt = $pdo->query("SELECT kanban_columns FROM configuracion WHERE id = 1");
$config = $stmt->fetch(PDO::FETCH_ASSOC);
// Fetch Kanban columns
$stmt_columns = $pdo->query('SELECT id, nombre FROM kanban_columns ORDER BY orden ASC');
$columns = $stmt_columns->fetchAll(PDO::FETCH_ASSOC);
// If no columns exist, create default ones
if (empty($columns)) {
$default_columns = ['Backlog', 'En Proceso', 'Hecho'];
$stmt_insert = $pdo->prepare('INSERT INTO kanban_columns (nombre, orden) VALUES (?, ?)');
foreach ($default_columns as $index => $name) {
$stmt_insert->execute([$name, $index + 1]);
$columns_to_display = [];
if ($config && !empty($config['kanban_columns'])) {
$column_ids = json_decode($config['kanban_columns'], true);
if (!empty($column_ids)) {
$placeholders = implode(',', array_fill(0, count($column_ids), '?'));
$stmt = $pdo->prepare("SELECT id, nombre FROM kanban_columns WHERE id IN ($placeholders)");
$stmt->execute($column_ids);
$columns_to_display = $stmt->fetchAll(PDO::FETCH_ASSOC);
}
// Re-fetch columns
$stmt_columns = $pdo->query('SELECT id, nombre FROM kanban_columns ORDER BY orden ASC');
$columns = $stmt_columns->fetchAll(PDO::FETCH_ASSOC);
}
// Fetch info cards and group them by column
$stmt_cards = $pdo->query('
SELECT
ip.id,
ip.texto_informativo,
ip.imagen_url,
ip.column_id,
p.nombre as producto_nombre
FROM
info_productos ip
LEFT JOIN
products p ON ip.producto_id = p.id
ORDER BY
ip.id ASC
');
$cards = $stmt_cards->fetchAll(PDO::FETCH_ASSOC);
$cards_by_column = [];
foreach ($cards as $card) {
$column_id = $card['column_id'] ?: 'unassigned';
$cards_by_column[$column_id][] = $card;
if (empty($columns_to_display)) {
$stmt = $pdo->query("SELECT id, nombre FROM kanban_columns");
$columns_to_display = $stmt->fetchAll(PDO::FETCH_ASSOC);
}
// Obtener todos los productos de información
$stmt = $pdo->query("SELECT ip.*, p.nombre as producto_nombre FROM info_productos ip LEFT JOIN products p ON ip.product_id = p.id");
$items = $stmt->fetchAll(PDO::FETCH_ASSOC);
$pageTitle = "Kanban de Productos";
$pageDescription = "Tablero Kanban para visualizar la información de los productos.";
include 'layout_header.php';
?>
<style>
@keyframes marquee {
0% { transform: translateX(-100%); }
100% { transform: translateX(100%); }
}
.motivational-banner {
background-color: rgba(255, 255, 255, 0.2);
color: #333;
padding: 15px 0;
margin: 0 auto 20px auto;
border-radius: 8px;
max-width: 80%;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
font-style: italic;
font-weight: 500;
overflow: hidden;
white-space: nowrap;
}
.motivational-banner p {
display: inline-block;
padding-left: 100%;
animation: marquee 15s linear infinite;
margin: 0;
}
.kanban-board {
display: flex;
overflow-x: auto;
overflow-y: hidden;
padding: 20px 10px;
background: linear-gradient(to right, #e8f5e9, #c8e6c9);
border-radius: 8px;
height: calc(100vh - 200px);
}
.kanban-column {
flex: 1 0 300px;
margin: 0 10px;
background-color: rgba(255, 255, 255, 0.7);
backdrop-filter: blur(5px);
border-radius: 8px;
padding: 10px;
display: flex;
flex-direction: column;
max-height: 100%;
}
.kanban-cards-container {
overflow-y: auto;
flex-grow: 1;
padding-right: 5px;
}
.kanban-column-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
.kanban-column-header h5 {
font-size: 1rem;
font-weight: bold;
margin: 0;
}
.kanban-card {
background-color: #fff;
border-radius: 3px;
box-shadow: 0 1px 0 rgba(9,30,66,.25);
padding: 10px;
margin-bottom: 10px;
}
.kanban-card img {
max-width: 100%;
border-radius: 3px;
}
</style>
<div class="container-fluid mt-4">
<div class="motivational-banner">
<p><?php echo htmlspecialchars($banner_text); ?></p>
</div>
<div class="d-flex justify-content-between align-items-center mb-3">
<div>
<h2>Tablero Kanban</h2>
<p>Visualiza el flujo de trabajo de la información de tus productos.</p>
<div class="row">
<div class="col-12">
<div class="d-flex justify-content-between align-items-center mb-3">
<h1><?php echo $pageTitle; ?></h1>
</div>
<p><?php echo $pageDescription; ?></p>
</div>
</div>
<div class="kanban-board">
<?php foreach ($columns as $column) : ?>
<div class="kanban-column" data-column-id="<?= $column['id'] ?>">
<div class="kanban-column-header">
<h5><?= htmlspecialchars($column['nombre']) ?></h5>
</div>
<div class="kanban-cards-container">
<?php if (isset($cards_by_column[$column['id']])) : ?>
<?php foreach ($cards_by_column[$column['id']] as $card) : ?>
<div class="kanban-card" data-card-id="<?= $card['id'] ?>">
<?php if (!empty($card['imagen_url'])) : ?>
<img src="<?= htmlspecialchars($card['imagen_url']) ?>" class="card-img-top" alt="Imagen del producto">
<?php endif; ?>
<div class="card-body" style="padding: 10px 0 0 0;">
<?php if (!empty($card['producto_nombre'])) : ?>
<h6 class="card-title" style="margin-bottom: 5px; font-weight: bold;"><?= htmlspecialchars($card['producto_nombre']) ?></h6>
<?php endif; ?>
<p class="card-text" id="texto-info-<?= $card['id'] ?>" style="font-size: 0.9rem;"><?= nl2br(htmlspecialchars($card['texto_informativo'])) ?></p>
<button class="btn btn-sm btn-outline-secondary mt-2" onclick="copiarTexto(this, 'texto-info-<?= $card['id'] ?>')">
<i class="fas fa-copy"></i> Copiar Texto
</button>
</div>
<div class="row">
<div class="col-12">
<div class="kanban-board-container">
<div class="kanban-board">
<?php foreach ($columns_to_display as $column): ?>
<div class="kanban-column" data-column-id="<?php echo $column['nombre']; ?>">
<div class="kanban-column-header">
<h5><?php echo htmlspecialchars($column['nombre']); ?></h5>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
</div>
<?php endforeach; ?>
<!-- Unassigned column -->
<div class="kanban-column" data-column-id="unassigned">
<div class="kanban-column-header">
<h5>Sin Asignar</h5>
</div>
<div class="kanban-cards-container">
<?php if (isset($cards_by_column['unassigned'])) : ?>
<?php foreach ($cards_by_column['unassigned'] as $card) : ?>
<div class="kanban-card" data-card-id="<?= $card['id'] ?>">
<?php if (!empty($card['imagen_url'])) : ?>
<img src="<?= htmlspecialchars($card['imagen_url']) ?>" class="card-img-top" alt="Imagen del producto">
<?php endif; ?>
<div class="card-body" style="padding: 10px 0 0 0;">
<?php if (!empty($card['producto_nombre'])) : ?>
<h6 class="card-title" style="margin-bottom: 5px; font-weight: bold;"><?= htmlspecialchars($card['producto_nombre']) ?></h6>
<?php endif; ?>
<p class="card-text" id="texto-info-<?= $card['id'] ?>" style="font-size: 0.9rem;"><?= nl2br(htmlspecialchars($card['texto_informativo'])) ?></p>
<button class="btn btn-sm btn-outline-secondary mt-2" onclick="copiarTexto(this, 'texto-info-<?= $card['id'] ?>')">
<i class="fas fa-copy"></i> Copiar Texto
</button>
<div class="kanban-column-body">
<?php foreach ($items as $item): ?>
<?php if ($item['estado_kanban'] == $column['nombre']): ?>
<div class="kanban-card" data-item-id="<?php echo $item['id']; ?>">
<div class="card-body">
<?php if (!empty($item['imagen'])): ?>
<img src="assets/uploads/info_images/<?php echo htmlspecialchars($item['imagen']); ?>" class="card-img-top mb-2" alt="Imagen del producto">
<?php endif; ?>
<h6 class="card-title"><?php echo htmlspecialchars($item['titulo']); ?></h6>
<p class="card-text small"><?php echo htmlspecialchars($item['producto_nombre']); ?></p>
<button class="btn btn-sm btn-secondary" onclick="copiarTexto(this)">Copiar Texto</button>
<div class="texto-a-copiar" style="display:none;"><?php echo htmlspecialchars($item['texto']); ?></div>
</div>
</div>
<?php endif; ?>
<?php endforeach; ?>
</div>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
</div>
</div>
</div>
</div>
<style>
.kanban-board-container {
overflow-x: auto;
padding-bottom: 15px;
}
.kanban-board {
display: flex;
gap: 15px;
min-width: max-content;
}
.kanban-column {
width: 300px;
flex-shrink: 0;
background-color: #f4f5f7;
border-radius: 3px;
}
.kanban-column-header {
padding: 10px 15px;
border-bottom: 1px solid #ddd;
}
.kanban-column-body {
padding: 10px;
min-height: 400px;
overflow-y: auto;
}
.kanban-card {
background-color: #fff;
border: 1px solid #ddd;
border-radius: 3px;
padding: 10px;
margin-bottom: 10px;
cursor: grab;
}
</style>
<script>
function copiarTexto(button, elementId) {
const textToCopy = document.getElementById(elementId).innerText;
navigator.clipboard.writeText(textToCopy).then(() => {
const originalIcon = 'fas fa-copy';
const originalText = ' Copiar Texto';
button.querySelector('i').className = 'fas fa-check';
button.lastChild.nodeValue = ' ¡Copiado!';
setTimeout(() => {
button.querySelector('i').className = originalIcon;
button.lastChild.nodeValue = originalText;
}, 2000);
}).catch(err => {
console.error('Error al copiar texto: ', err);
alert('Error al copiar texto.');
});
function copiarTexto(button) {
var cardBody = button.closest('.card-body');
var texto = cardBody.querySelector('.texto-a-copiar').innerText;
var tempInput = document.createElement('textarea');
tempInput.style.position = 'absolute';
tempInput.style.left = '-9999px';
tempInput.value = texto;
document.body.appendChild(tempInput);
tempInput.select();
document.execCommand('copy');
document.body.removeChild(tempInput);
button.innerText = 'Copiado!';
setTimeout(function() {
button.innerText = 'Copiar Texto';
}, 2000);
}
</script>
<?php require_once 'layout_footer.php'; ?>
<?php include 'layout_footer.php'; ?>

View File

@ -165,6 +165,12 @@ $navItems = [
'icon' => 'fa-users',
'text' => 'Gestionar Usuarios',
'roles' => ['Administrador', 'admin']
],
'configuracion' => [
'url' => 'configuracion.php',
'icon' => 'fa-cog',
'text' => 'Configuración',
'roles' => ['Administrador', 'admin']
]
];

View File

@ -10,9 +10,9 @@ if ($data) {
// Column name validation to prevent SQL injection
$allowed_columns = [
'bcp_yape', 'b_nacion', 'interbank', 'bbva', 'otros_ingresos',
'c_pedidos', 'bcp_yape', 'b_nacion', 'interbank', 'bbva', 'otros_ingresos',
'tu1', 'tu2', 'tu3', 'fl1', 'fl2', 'fl3',
'rc_envio', 'rc_contraent', 'sc', 'total_inversion_publicitaria'
'rc_contraent', 'sc', 'total_inversion_publicitaria'
];
if (in_array($columna, $allowed_columns)) {