diff --git a/add_column.php b/add_column.php index efae892..b070ae8 100644 --- a/add_column.php +++ b/add_column.php @@ -1,21 +1,65 @@ query('SELECT MAX(orden) AS max_orden FROM kanban_columns'); - $max_orden = $stmt_max_order->fetchColumn(); - $new_orden = $max_orden + 1; - - $stmt = $pdo->prepare('INSERT INTO kanban_columns (nombre, orden) VALUES (?, ?)'); - $stmt->execute([$nombre, $new_orden]); - } +// Asegurarse de que el usuario sea administrador +if ($_SESSION['user_role'] !== 'Administrador' && $_SESSION['user_role'] !== 'admin') { + echo "
Acceso denegado.
"; + require_once 'layout_footer.php'; + exit(); } -header('Location: kanban.php'); -exit; +$message = ''; + +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $nombre = trim($_POST['nombre'] ?? ''); + + if (!empty($nombre)) { + try { + $pdo = db(); + + // Find the current max order value + $stmt_max_order = $pdo->query('SELECT MAX(orden) AS max_orden FROM kanban_columns'); + $max_orden = $stmt_max_order->fetchColumn(); + $new_orden = ($max_orden === null) ? 0 : $max_orden + 1; + + $stmt = $pdo->prepare('INSERT INTO kanban_columns (nombre, orden) VALUES (?, ?)'); + + if ($stmt->execute([$nombre, $new_orden])) { + // Usar una variable de sesión para mostrar el mensaje en la página de configuración + $_SESSION['success_message'] = "Columna '" . htmlspecialchars($nombre) . "' añadida correctamente."; + header('Location: configuracion.php'); + exit; + } else { + $message = "
Error al añadir la columna.
"; + } + } catch (PDOException $e) { + $message = "
Error de base de datos: " . $e->getMessage() . "
"; + } + } else { + $message = "
El nombre de la columna no puede estar vacío.
"; + } +} +?> + +
+
+
+

Añadir Nueva Columna al Kanban

+
+
+ +
+
+ + +
+ + Cancelar +
+
+
+
+ + \ No newline at end of file diff --git a/assets/uploads/vouchers/69827290a0799-WhatsApp Image 2026-02-03 at 5.03.49 PM.jpeg b/assets/uploads/vouchers/69827290a0799-WhatsApp Image 2026-02-03 at 5.03.49 PM.jpeg new file mode 100644 index 0000000..732cfac Binary files /dev/null and b/assets/uploads/vouchers/69827290a0799-WhatsApp Image 2026-02-03 at 5.03.49 PM.jpeg differ diff --git a/assets/uploads/vouchers/6982730d55a5d-WhatsApp Image 2026-02-03 at 4.54.16 PM.jpeg b/assets/uploads/vouchers/6982730d55a5d-WhatsApp Image 2026-02-03 at 4.54.16 PM.jpeg new file mode 100644 index 0000000..439eeba Binary files /dev/null and b/assets/uploads/vouchers/6982730d55a5d-WhatsApp Image 2026-02-03 at 4.54.16 PM.jpeg differ diff --git a/assets/uploads/vouchers/69835539cfaef-WhatsApp Image 2026-02-04 at 7.41.48 AM.jpeg b/assets/uploads/vouchers/69835539cfaef-WhatsApp Image 2026-02-04 at 7.41.48 AM.jpeg new file mode 100644 index 0000000..232fac3 Binary files /dev/null and b/assets/uploads/vouchers/69835539cfaef-WhatsApp Image 2026-02-04 at 7.41.48 AM.jpeg differ diff --git a/assets/uploads/vouchers/6983736369da6-WhatsApp Image 2026-02-04 at 11.22.59 AM.jpeg b/assets/uploads/vouchers/6983736369da6-WhatsApp Image 2026-02-04 at 11.22.59 AM.jpeg new file mode 100644 index 0000000..83d9c63 Binary files /dev/null and b/assets/uploads/vouchers/6983736369da6-WhatsApp Image 2026-02-04 at 11.22.59 AM.jpeg differ diff --git a/assets/uploads/vouchers/698393768f082-WhatsApp Image 2026-02-04 at 1.39.36 PM.jpeg b/assets/uploads/vouchers/698393768f082-WhatsApp Image 2026-02-04 at 1.39.36 PM.jpeg new file mode 100644 index 0000000..c082688 Binary files /dev/null and b/assets/uploads/vouchers/698393768f082-WhatsApp Image 2026-02-04 at 1.39.36 PM.jpeg differ diff --git a/assets/uploads/vouchers/698397c3dc725-WhatsApp Image 2026-02-04 at 2.01.26 PM.jpeg b/assets/uploads/vouchers/698397c3dc725-WhatsApp Image 2026-02-04 at 2.01.26 PM.jpeg new file mode 100644 index 0000000..3a5bfeb Binary files /dev/null and b/assets/uploads/vouchers/698397c3dc725-WhatsApp Image 2026-02-04 at 2.01.26 PM.jpeg differ diff --git a/assets/uploads/vouchers/6983a7b975527-WhatsApp Image 2026-02-04 at 2.44.15 PM.jpeg b/assets/uploads/vouchers/6983a7b975527-WhatsApp Image 2026-02-04 at 2.44.15 PM.jpeg new file mode 100644 index 0000000..db52e32 Binary files /dev/null and b/assets/uploads/vouchers/6983a7b975527-WhatsApp Image 2026-02-04 at 2.44.15 PM.jpeg differ diff --git a/assets/uploads/vouchers/6983c48e55dd7-WhatsApp Image 2026-02-03 at 6.33.32 PM.jpeg b/assets/uploads/vouchers/6983c48e55dd7-WhatsApp Image 2026-02-03 at 6.33.32 PM.jpeg new file mode 100644 index 0000000..3486119 Binary files /dev/null and b/assets/uploads/vouchers/6983c48e55dd7-WhatsApp Image 2026-02-03 at 6.33.32 PM.jpeg differ diff --git a/assets/uploads/vouchers/6983c4c27f416-WhatsApp Image 2026-02-04 at 3.49.15 PM.jpeg b/assets/uploads/vouchers/6983c4c27f416-WhatsApp Image 2026-02-04 at 3.49.15 PM.jpeg new file mode 100644 index 0000000..b232f70 Binary files /dev/null and b/assets/uploads/vouchers/6983c4c27f416-WhatsApp Image 2026-02-04 at 3.49.15 PM.jpeg differ diff --git a/assets/uploads/vouchers/6983c4ff9424a-WhatsApp Image 2026-02-04 at 1.16.31 PM.jpeg b/assets/uploads/vouchers/6983c4ff9424a-WhatsApp Image 2026-02-04 at 1.16.31 PM.jpeg new file mode 100644 index 0000000..364af3a Binary files /dev/null and b/assets/uploads/vouchers/6983c4ff9424a-WhatsApp Image 2026-02-04 at 1.16.31 PM.jpeg differ diff --git a/assets/uploads/vouchers/6983c544a2f3c-WhatsApp Image 2026-02-03 at 4.37.06 PM.jpeg b/assets/uploads/vouchers/6983c544a2f3c-WhatsApp Image 2026-02-03 at 4.37.06 PM.jpeg new file mode 100644 index 0000000..879218a Binary files /dev/null and b/assets/uploads/vouchers/6983c544a2f3c-WhatsApp Image 2026-02-03 at 4.37.06 PM.jpeg differ diff --git a/assets/uploads/vouchers/6983c57c49201-WhatsApp Image 2026-02-04 at 4.41.12 PM.jpeg b/assets/uploads/vouchers/6983c57c49201-WhatsApp Image 2026-02-04 at 4.41.12 PM.jpeg new file mode 100644 index 0000000..475cfd7 Binary files /dev/null and b/assets/uploads/vouchers/6983c57c49201-WhatsApp Image 2026-02-04 at 4.41.12 PM.jpeg differ diff --git a/assets/uploads/vouchers/6983cf375d8a4-WhatsApp Image 2026-01-31 at 6.01.08 PM.jpeg b/assets/uploads/vouchers/6983cf375d8a4-WhatsApp Image 2026-01-31 at 6.01.08 PM.jpeg new file mode 100644 index 0000000..fef34e2 Binary files /dev/null and b/assets/uploads/vouchers/6983cf375d8a4-WhatsApp Image 2026-01-31 at 6.01.08 PM.jpeg differ diff --git a/configuracion.php b/configuracion.php index 9e42745..1767628 100644 --- a/configuracion.php +++ b/configuracion.php @@ -12,63 +12,119 @@ if ($_SESSION['user_role'] !== 'Administrador' && $_SESSION['user_role'] !== 'ad $conn = db(); -// Obtener la configuración actual -$query = "SELECT * FROM configuracion WHERE id = 1"; -$result = $conn->query($query); -$config = $result->fetch(PDO::FETCH_ASSOC); +// --- Gestión de Columnas --- -// 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' -]; +// Obtener todas las columnas del Kanban para la tabla de gestión +$query_manage_columns = "SELECT * FROM kanban_columns ORDER BY orden ASC"; +$stmt_manage_columns = $conn->query($query_manage_columns); +$management_columns = $stmt_manage_columns->fetchAll(PDO::FETCH_ASSOC); + + +// --- Visibilidad de Columnas --- + +// Obtener la configuración actual de visibilidad +$query_config = "SELECT * FROM configuracion WHERE id = 1"; +$result_config = $conn->query($query_config); +$config = $result_config->fetch(PDO::FETCH_ASSOC); $visible_columns = $config ? json_decode($config['kanban_columns'], true) : []; -if ($_SERVER['REQUEST_METHOD'] === 'POST') { +if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['save_visibility'])) { $new_visible_columns = $_POST['columns'] ?? []; $json_columns = json_encode($new_visible_columns); - $update_query = "UPDATE configuracion SET kanban_columns = :kanban_columns WHERE id = 1"; + // Usar INSERT ... ON DUPLICATE KEY UPDATE para seguridad + $update_query = "INSERT INTO configuracion (id, kanban_columns) VALUES (1, :kanban_columns) + ON DUPLICATE KEY UPDATE kanban_columns = :kanban_columns"; $stmt = $conn->prepare($update_query); $stmt->bindParam(':kanban_columns', $json_columns); if ($stmt->execute()) { - echo "
Configuración guardada correctamente.
"; - $visible_columns = $new_visible_columns; + echo "
Configuración de visibilidad guardada.
"; + $visible_columns = $new_visible_columns; // Actualizar para mostrar el cambio inmediatamente } else { - echo "
Error al guardar la configuración.
"; + echo "
Error al guardar la configuración de visibilidad.
"; } } + +// Obtener todas las columnas de kanban_columns para el formulario de visibilidad +$all_db_columns_query = "SELECT nombre FROM kanban_columns ORDER BY orden ASC"; +$stmt_all_db_columns = $conn->query($all_db_columns_query); +$available_columns_for_visibility = $stmt_all_db_columns->fetchAll(PDO::FETCH_COLUMN); + ?>
-

Configuración de Columnas del Kanban

-

Selecciona las columnas que deseas que sean visibles en el tablero Kanban.

- -
-
- $name): ?> -
-
- > - -
-
- + + +
+
+

Gestionar Columnas del Kanban

+
+

Aquí puedes añadir, editar y eliminar las columnas que se usarán en el tablero Kanban.

+ + Añadir Nueva Columna + + + + + + + + + + + + + + + + + + + + + + + +
Nombre de la ColumnaAcciones
No hay columnas definidas.
+ + Editar + + + Eliminar + +
+
+
- - + +
+
+

Configurar Visibilidad de Columnas

+
+
+

Selecciona las columnas que deseas que sean visibles en el tablero Kanban.

+
+ +
+ +
+
+ > + +
+
+ +
+ + +
+
+
- \ No newline at end of file + diff --git a/delete_column.php b/delete_column.php index 1eae0d7..44e73aa 100644 --- a/delete_column.php +++ b/delete_column.php @@ -1,21 +1,45 @@ prepare("SELECT nombre FROM kanban_columns WHERE id = ?"); + $stmt_name->execute([$id]); + $column_name = $stmt_name->fetchColumn(); + + // Primero, actualiza info_productos para desasociar las tarjetas de la columna $stmt_update = $pdo->prepare('UPDATE info_productos SET column_id = NULL WHERE column_id = ?'); $stmt_update->execute([$id]); - // Then, delete the column + // Luego, elimina la columna $stmt_delete = $pdo->prepare('DELETE FROM kanban_columns WHERE id = ?'); - $stmt_delete->execute([$id]); + + if ($stmt_delete->execute([$id])) { + $_SESSION['success_message'] = "Columna '" . htmlspecialchars($column_name) . "' eliminada correctamente."; + } else { + $_SESSION['error_message'] = "Error al eliminar la columna."; + } + + } catch (PDOException $e) { + $_SESSION['error_message'] = "Error de base de datos: " . $e->getMessage(); } +} else { + $_SESSION['error_message'] = "ID de columna no válido."; } -header('Location: kanban.php'); +header('Location: configuracion.php'); exit; +?> \ No newline at end of file diff --git a/edit_column.php b/edit_column.php index efa9e41..d426f79 100644 --- a/edit_column.php +++ b/edit_column.php @@ -1,18 +1,82 @@ Acceso denegado.
"; + require_once 'layout_footer.php'; + exit(); +} - $db = db(); - $stmt = $db->prepare("UPDATE kanban_columns SET name = ? WHERE id = ?"); - $stmt->bind_param('si', $nombre, $id); - $stmt->execute(); +$message = ''; +$column = null; - $_SESSION['success_message'] = "Columna actualizada exitosamente."; - header('Location: info_producto.php'); +if (!isset($_GET['id']) || !is_numeric($_GET['id'])) { + header('Location: configuracion.php'); exit; } -?> \ No newline at end of file + +$column_id = $_GET['id']; + +try { + $pdo = db(); + + // Procesar el formulario cuando se envía + if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $nombre = trim($_POST['nombre'] ?? ''); + + if (!empty($nombre)) { + $stmt = $pdo->prepare('UPDATE kanban_columns SET nombre = ? WHERE id = ?'); + if ($stmt->execute([$nombre, $column_id])) { + $_SESSION['success_message'] = "Columna actualizada a '" . htmlspecialchars($nombre) . "'."; + header('Location: configuracion.php'); + exit; + } else { + $message = "
Error al actualizar la columna.
"; + } + } else { + $message = "
El nombre de la columna no puede estar vacío.
"; + } + } + + // Obtener los datos de la columna para mostrar en el formulario + $stmt = $pdo->prepare('SELECT * FROM kanban_columns WHERE id = ?'); + $stmt->execute([$column_id]); + $column = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!$column) { + echo "
La columna no existe.
"; + require_once 'layout_footer.php'; + exit; + } + +} catch (PDOException $e) { + $message = "
Error de base de datos: " . $e->getMessage() . "
"; +} + +?> + +
+
+
+

Editar Columna del Kanban

+
+
+ + +
+
+ + +
+ + Cancelar +
+ +
+
+
+ + diff --git a/kanban.php b/kanban.php index 8449089..f0d3171 100644 --- a/kanban.php +++ b/kanban.php @@ -1,4 +1,5 @@ query("SELECT kanban_columns FROM configuracion WHERE id = 1"); -$config = $stmt->fetch(PDO::FETCH_ASSOC); +// 1. Obtener todas las columnas del Kanban +$stmt_cols = $pdo->query("SELECT id, nombre FROM kanban_columns ORDER BY orden, id"); +$columns_to_display = $stmt_cols->fetchAll(PDO::FETCH_ASSOC); -$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); +// 2. Obtener todos los productos de información, uniéndolos con productos y columnas +$stmt_items = $pdo->query(" + SELECT + ip.id, + ip.texto_informativo, + ip.imagen_url, + p.nombre as producto_nombre, + kc.nombre as estado_kanban + FROM + info_productos ip + LEFT JOIN + products p ON ip.producto_id = p.id + LEFT JOIN + kanban_columns kc ON ip.column_id = kc.id + ORDER BY + ip.orden, ip.id +"); +$items = $stmt_items->fetchAll(PDO::FETCH_ASSOC); + +// 3. Agrupar items por el nombre de la columna (estado_kanban) +$items_by_column = []; +if ($items) { + foreach ($items as $item) { + if (!empty($item['estado_kanban'])) { + $items_by_column[$item['estado_kanban']][] = $item; + } } } -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."; @@ -58,26 +68,31 @@ include 'layout_header.php';
-
+
- - + +
- - Imagen del producto + + Imagen del producto -
-

- - + +
+ +

+ + +
- - + +
@@ -101,45 +116,80 @@ include 'layout_header.php'; width: 300px; flex-shrink: 0; background-color: #f4f5f7; - border-radius: 3px; + border-radius: 8px; + box-shadow: 0 1px 3px rgba(0,0,0,0.1); } .kanban-column-header { - padding: 10px 15px; - border-bottom: 1px solid #ddd; + padding: 12px 15px; + border-bottom: 1px solid #e0e0e0; + background-color: #fff; + border-top-left-radius: 8px; + border-top-right-radius: 8px; } .kanban-column-body { padding: 10px; - min-height: 400px; - overflow-y: auto; + min-height: 200px; /* Altura mínima por si la columna está vacía */ + max-height: 70vh; /* Altura máxima del 70% de la pantalla */ + overflow-y: auto; /* Scroll vertical si el contenido excede la altura */ } .kanban-card { background-color: #fff; border: 1px solid #ddd; - border-radius: 3px; - padding: 10px; + border-radius: 6px; + padding: 15px; margin-bottom: 10px; cursor: grab; + transition: box-shadow 0.2s ease; + } + .kanban-card:hover { + box-shadow: 0 4px 8px rgba(0,0,0,0.15); + } + .card-title { + font-weight: 600; + margin-bottom: 0; /* Ajustado para dar espacio al botón */ }