getMessage(); } $pageTitle = "Recursos Humanos - Manuales y Capacitación"; $pageDescription = "Repositorio interno de manuales, procedimientos y material de capacitación: PDF/Word, búsqueda y descargas."; $categoriasValidas = [ 'Introducción a la Empresa', 'Manual de Ventas', 'Guía de Objeciones', 'Script Comercial', 'Atención al Cliente', 'Manual de Almacén', 'Manual de Inventario', 'Procedimientos Operativos', 'Políticas Internas', 'Marketing', 'Otros' ]; $areasValidas = ['General', 'Ventas', 'Almacén', 'Marketing', 'Administración']; $estadosValidos = ['Activo', 'Inactivo']; function hr_move_uploaded_file(array $file, string $uploadDirAbs, array $allowedExts): string { $error = $file['error'] ?? UPLOAD_ERR_NO_FILE; if ($error !== UPLOAD_ERR_OK) { throw new RuntimeException('Error al cargar el archivo.'); } $size = (int)($file['size'] ?? 0); $maxBytes = 8 * 1024 * 1024; if ($size <= 0) { throw new RuntimeException('Archivo vacío.'); } if ($size > $maxBytes) { throw new RuntimeException('El archivo excede el tamaño máximo permitido (8MB).'); } $origName = (string)($file['name'] ?? 'archivo'); $ext = strtolower(pathinfo($origName, PATHINFO_EXTENSION)); if ($ext === '' || !in_array($ext, $allowedExts, true)) { throw new RuntimeException('Tipo de archivo no permitido.'); } if (!is_dir($uploadDirAbs)) { mkdir($uploadDirAbs, 0775, true); } $base = pathinfo($origName, PATHINFO_FILENAME); $base = preg_replace('/[^A-Za-z0-9._-]+/', '_', $base); if (!$base) { $base = 'archivo'; } $newName = time() . '_' . $base . '.' . $ext; $targetAbs = rtrim($uploadDirAbs, '/\\') . DIRECTORY_SEPARATOR . $newName; if (!move_uploaded_file($file['tmp_name'], $targetAbs)) { throw new RuntimeException('No se pudo guardar el archivo en el servidor.'); } $uploadDirRel = str_replace(__DIR__ . DIRECTORY_SEPARATOR, '', rtrim($uploadDirAbs, '/\\')); return $uploadDirRel . '/' . $newName; } $notice = null; $noticeType = 'success'; if (!empty($schemaInitError)) { $notice = 'Error al preparar las tablas de Recursos Humanos.'; $noticeType = 'danger'; error_log('HR schema init error (manuales): ' . $schemaInitError); } if (isset($_GET['success']) && $_GET['success'] === '1') { $notice = 'Manual guardado correctamente.'; $noticeType = 'success'; } if (isset($_GET['estado_updated']) && $_GET['estado_updated'] === '1') { $notice = 'Estado del manual actualizado correctamente.'; $noticeType = 'success'; } if ($_SERVER['REQUEST_METHOD'] === 'POST') { $action = $_POST['action'] ?? ''; if ($action === 'add_manual') { try { $titulo = trim((string)($_POST['titulo'] ?? '')); $categoria = (string)($_POST['categoria'] ?? ''); $area = (string)($_POST['area'] ?? 'General'); $descripcion = trim((string)($_POST['descripcion'] ?? '')); $fecha_creacion = $_POST['fecha_creacion'] ?? date('Y-m-d'); $estado = (string)($_POST['estado'] ?? 'Activo'); if ($titulo === '' || $categoria === '' || $area === '') { throw new RuntimeException('Faltan campos obligatorios.'); } if (!in_array($categoria, $categoriasValidas, true)) { throw new RuntimeException('Categoría inválida.'); } if (!in_array($area, $areasValidas, true)) { throw new RuntimeException('Área inválida.'); } if (!in_array($estado, $estadosValidos, true)) { throw new RuntimeException('Estado inválido.'); } $pdfPath = null; $wordPath = null; $pdfFile = $_FILES['archivo_pdf'] ?? null; if ($pdfFile && !empty($pdfFile['name']) && ($pdfFile['error'] ?? UPLOAD_ERR_NO_FILE) === UPLOAD_ERR_OK) { $pdfUploadDirAbs = __DIR__ . '/assets/uploads/hr/manuales/pdf/'; $pdfPath = hr_move_uploaded_file($pdfFile, $pdfUploadDirAbs, ['pdf']); } $wordFile = $_FILES['archivo_word'] ?? null; if ($wordFile && !empty($wordFile['name']) && ($wordFile['error'] ?? UPLOAD_ERR_NO_FILE) === UPLOAD_ERR_OK) { $wordUploadDirAbs = __DIR__ . '/assets/uploads/hr/manuales/word/'; $wordPath = hr_move_uploaded_file($wordFile, $wordUploadDirAbs, ['doc', 'docx']); } if (empty($pdfPath) && empty($wordPath)) { throw new RuntimeException('Sube al menos un archivo: PDF y/o Word.'); } $stmt = $pdo->prepare('INSERT INTO hr_manuales (titulo, categoria, area, descripcion, archivo_pdf_path, archivo_word_path, fecha_creacion, estado) VALUES (?, ?, ?, ?, ?, ?, ?, ?)'); $stmt->execute([ $titulo, $categoria, $area, $descripcion, $pdfPath, $wordPath, $fecha_creacion, $estado, ]); header('Location: hr_manuales.php?success=1'); exit; } catch (Throwable $e) { $notice = 'No se pudo guardar el manual: ' . htmlspecialchars($e->getMessage(), ENT_QUOTES, 'UTF-8'); $noticeType = 'danger'; } } if ($action === 'update_manual_estado') { try { $id = (int)($_POST['id'] ?? 0); $nuevoEstado = (string)($_POST['estado'] ?? ''); if ($id <= 0) { throw new RuntimeException('ID inválido.'); } if (!in_array($nuevoEstado, $estadosValidos, true)) { throw new RuntimeException('Estado inválido.'); } $stmt = $pdo->prepare('UPDATE hr_manuales SET estado = ? WHERE id = ?'); $stmt->execute([$nuevoEstado, $id]); header('Location: hr_manuales.php?estado_updated=1'); exit; } catch (Throwable $e) { $notice = 'No se pudo actualizar el estado del manual.'; $noticeType = 'danger'; } } } // --- Filtros --- $q = trim((string)($_GET['q'] ?? '')); $fCategoria = (string)($_GET['categoria'] ?? 'Todas'); $fArea = (string)($_GET['area'] ?? 'Todas'); $fEstado = (string)($_GET['estado'] ?? 'Activo'); if ($fCategoria !== 'Todas' && !in_array($fCategoria, $categoriasValidas, true)) { $fCategoria = 'Todas'; } if ($fArea !== 'Todas' && !in_array($fArea, $areasValidas, true)) { $fArea = 'Todas'; } if (!in_array($fEstado, array_merge(['Todos'], $estadosValidos), true)) { $fEstado = 'Activo'; } $where = []; $params = []; if ($q !== '') { $where[] = 'titulo LIKE ?'; $params[] = '%' . $q . '%'; } if ($fCategoria !== 'Todas') { $where[] = 'categoria = ?'; $params[] = $fCategoria; } if ($fArea !== 'Todas') { $where[] = 'area = ?'; $params[] = $fArea; } if ($fEstado !== 'Todos') { $where[] = 'estado = ?'; $params[] = $fEstado; } $sql = 'SELECT id, titulo, categoria, area, descripcion, archivo_pdf_path, archivo_word_path, fecha_creacion, estado FROM hr_manuales'; if (!empty($where)) { $sql .= ' WHERE ' . implode(' AND ', $where); } $sql .= ' ORDER BY fecha_creacion DESC, id DESC'; $manuales = []; try { $stmt = $pdo->prepare($sql); $stmt->execute($params); $manuales = $stmt->fetchAll(PDO::FETCH_ASSOC); } catch (PDOException $e) { error_log('HR Manuales DB error: ' . $e->getMessage()); $notice = 'No se pudo cargar el repositorio de manuales.'; $noticeType = 'danger'; } $fechaHoy = date('Y-m-d'); include 'layout_header.php'; ?>
| ID | Título | Categoría | Área | Fecha | Estado | Archivo | Descripción | Acciones |
|---|---|---|---|---|---|---|---|---|
| No hay manuales con los filtros seleccionados. | ||||||||
| = (int)$m['id']; ?> | = htmlspecialchars($m['titulo'] ?? '', ENT_QUOTES, 'UTF-8'); ?> | = htmlspecialchars($m['categoria'] ?? '', ENT_QUOTES, 'UTF-8'); ?> | = htmlspecialchars($m['area'] ?? '', ENT_QUOTES, 'UTF-8'); ?> | = htmlspecialchars($m['fecha_creacion'] ?? '', ENT_QUOTES, 'UTF-8'); ?> | = htmlspecialchars($m['estado'] ?? '', ENT_QUOTES, 'UTF-8'); ?> |
= htmlspecialchars($descripcion, ENT_QUOTES, 'UTF-8'); ?>
|
||