357 lines
16 KiB
PHP
357 lines
16 KiB
PHP
<?php
|
|
session_start();
|
|
|
|
$userRole = $_SESSION['user_role'] ?? '';
|
|
if (!isset($_SESSION['user_id']) || ($userRole !== 'Administrador' && $userRole !== 'admin')) {
|
|
header('Location: dashboard.php');
|
|
exit;
|
|
}
|
|
|
|
require_once 'db/config.php';
|
|
$pdo = db();
|
|
|
|
$schemaInitError = null;
|
|
require_once 'includes/hr_bootstrap.php';
|
|
try {
|
|
hr_ensure_schema($pdo);
|
|
} catch (Throwable $e) {
|
|
$schemaInitError = $e->getMessage();
|
|
}
|
|
|
|
$pageTitle = "Recursos Humanos - Colaboradores";
|
|
$pageDescription = "Gestión del personal: registro, área, cargo y estado.";
|
|
|
|
$areasValidas = ['Ventas', 'Almacén', 'Marketing', 'Administración', 'Finanzas', 'Gerencia'];
|
|
$estadosValidos = ['Activo', 'Inactivo', 'Suspendido'];
|
|
|
|
$notice = null;
|
|
$noticeType = 'success';
|
|
|
|
if (!empty($schemaInitError)) {
|
|
$notice = 'Error al preparar las tablas de Recursos Humanos.';
|
|
$noticeType = 'danger';
|
|
error_log('HR schema init error (colaboradores): ' . $schemaInitError);
|
|
}
|
|
|
|
if (isset($_GET['success']) && $_GET['success'] === '1') {
|
|
$notice = 'Colaborador guardado correctamente.';
|
|
$noticeType = 'success';
|
|
}
|
|
if (isset($_GET['estado_updated']) && $_GET['estado_updated'] === '1') {
|
|
$notice = 'Estado actualizado correctamente.';
|
|
$noticeType = 'success';
|
|
}
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
$action = $_POST['action'] ?? '';
|
|
|
|
if ($action === 'add_colaborador') {
|
|
try {
|
|
$nombre_completo = trim((string)($_POST['nombre_completo'] ?? ''));
|
|
$dni = trim((string)($_POST['dni'] ?? ''));
|
|
$celular = trim((string)($_POST['celular'] ?? ''));
|
|
$correo = trim((string)($_POST['correo'] ?? ''));
|
|
$area = (string)($_POST['area'] ?? '');
|
|
$cargo = trim((string)($_POST['cargo'] ?? ''));
|
|
$fecha_ingreso = $_POST['fecha_ingreso'] ?? date('Y-m-d');
|
|
$estado = (string)($_POST['estado'] ?? 'Activo');
|
|
$observaciones = trim((string)($_POST['observaciones'] ?? ''));
|
|
|
|
if ($nombre_completo === '' || $dni === '' || $celular === '' || $correo === '' || $cargo === '' || $area === '') {
|
|
throw new RuntimeException('Faltan campos obligatorios.');
|
|
}
|
|
if (!filter_var($correo, FILTER_VALIDATE_EMAIL)) {
|
|
throw new RuntimeException('Correo inválido.');
|
|
}
|
|
if (!in_array($area, $areasValidas, true)) {
|
|
throw new RuntimeException('Área inválida.');
|
|
}
|
|
if (!in_array($estado, $estadosValidos, true)) {
|
|
throw new RuntimeException('Estado inválido.');
|
|
}
|
|
|
|
$stmt = $pdo->prepare('INSERT INTO hr_colaboradores (nombre_completo, dni, celular, correo, area, cargo, fecha_ingreso, estado, observaciones) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)');
|
|
$stmt->execute([
|
|
$nombre_completo,
|
|
$dni,
|
|
$celular,
|
|
$correo,
|
|
$area,
|
|
$cargo,
|
|
$fecha_ingreso,
|
|
$estado,
|
|
$observaciones,
|
|
]);
|
|
|
|
header('Location: hr_colaboradores.php?success=1');
|
|
exit;
|
|
} catch (Throwable $e) {
|
|
$notice = 'No se pudo guardar el colaborador: ' . htmlspecialchars($e->getMessage(), ENT_QUOTES, 'UTF-8');
|
|
$noticeType = 'danger';
|
|
}
|
|
}
|
|
|
|
if ($action === 'update_colaborador_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_colaboradores SET estado = ? WHERE id = ?');
|
|
$stmt->execute([$nuevoEstado, $id]);
|
|
|
|
header('Location: hr_colaboradores.php?estado_updated=1');
|
|
exit;
|
|
} catch (Throwable $e) {
|
|
$notice = 'No se pudo actualizar el estado.';
|
|
$noticeType = 'danger';
|
|
}
|
|
}
|
|
}
|
|
|
|
// --- Filtros ---
|
|
$filter_area = (string)($_GET['area'] ?? 'Todas');
|
|
$filter_estado = (string)($_GET['estado'] ?? 'Todos');
|
|
$search = trim((string)($_GET['q'] ?? ''));
|
|
|
|
if ($filter_area !== 'Todas' && !in_array($filter_area, $areasValidas, true)) {
|
|
$filter_area = 'Todas';
|
|
}
|
|
|
|
if ($filter_estado !== 'Todos' && !in_array($filter_estado, $estadosValidos, true)) {
|
|
$filter_estado = 'Todos';
|
|
}
|
|
|
|
$where = [];
|
|
$params = [];
|
|
|
|
if ($filter_area !== 'Todas') {
|
|
$where[] = 'area = ?';
|
|
$params[] = $filter_area;
|
|
}
|
|
|
|
if ($filter_estado !== 'Todos') {
|
|
$where[] = 'estado = ?';
|
|
$params[] = $filter_estado;
|
|
}
|
|
|
|
if ($search !== '') {
|
|
$where[] = '(nombre_completo LIKE ? OR dni LIKE ? OR cargo LIKE ?)';
|
|
$like = '%' . $search . '%';
|
|
$params[] = $like;
|
|
$params[] = $like;
|
|
$params[] = $like;
|
|
}
|
|
|
|
$sql = 'SELECT id, nombre_completo, dni, celular, correo, area, cargo, fecha_ingreso, estado, observaciones FROM hr_colaboradores';
|
|
if (!empty($where)) {
|
|
$sql .= ' WHERE ' . implode(' AND ', $where);
|
|
}
|
|
$sql .= ' ORDER BY fecha_ingreso DESC, id DESC';
|
|
|
|
$colaboradores = [];
|
|
try {
|
|
$stmt = $pdo->prepare($sql);
|
|
$stmt->execute($params);
|
|
$colaboradores = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
} catch (PDOException $e) {
|
|
error_log('HR Colaboradores DB error: ' . $e->getMessage());
|
|
$notice = 'No se pudo cargar la lista de colaboradores.';
|
|
$noticeType = 'danger';
|
|
}
|
|
|
|
|
|
$fechaHoy = date('Y-m-d');
|
|
|
|
include 'layout_header.php';
|
|
?>
|
|
|
|
<div class="container-fluid mt-4">
|
|
<?php if ($notice): ?>
|
|
<div class="alert alert-<?= htmlspecialchars($noticeType, ENT_QUOTES, 'UTF-8'); ?> mb-4" role="alert">
|
|
<?= $notice; ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<div class="row g-4">
|
|
<div class="col-lg-4">
|
|
<div class="card shadow-sm">
|
|
<div class="card-header bg-light">
|
|
<h2 class="h5 mb-0">Nuevo Colaborador</h2>
|
|
</div>
|
|
<div class="card-body">
|
|
<form method="POST">
|
|
<input type="hidden" name="action" value="add_colaborador">
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label">Nombre Completo</label>
|
|
<input type="text" name="nombre_completo" class="form-control" required>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label">DNI</label>
|
|
<input type="text" name="dni" class="form-control" required>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label">Celular</label>
|
|
<input type="text" name="celular" class="form-control" required>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label">Correo</label>
|
|
<input type="email" name="correo" class="form-control" required>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label">Área</label>
|
|
<select name="area" class="form-select" required>
|
|
<?php foreach ($areasValidas as $a): ?>
|
|
<option value="<?= htmlspecialchars($a, ENT_QUOTES, 'UTF-8'); ?>"><?= htmlspecialchars($a, ENT_QUOTES, 'UTF-8'); ?></option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label">Cargo</label>
|
|
<input type="text" name="cargo" class="form-control" required>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label">Fecha de Ingreso</label>
|
|
<input type="date" name="fecha_ingreso" class="form-control" value="<?= htmlspecialchars($fechaHoy, ENT_QUOTES, 'UTF-8'); ?>" required>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label">Estado</label>
|
|
<select name="estado" class="form-select" required>
|
|
<?php foreach ($estadosValidos as $e): ?>
|
|
<option value="<?= htmlspecialchars($e, ENT_QUOTES, 'UTF-8'); ?>"><?= htmlspecialchars($e, ENT_QUOTES, 'UTF-8'); ?></option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label">Observaciones</label>
|
|
<textarea name="observaciones" class="form-control" rows="3"></textarea>
|
|
</div>
|
|
|
|
<button type="submit" class="btn btn-primary w-100">
|
|
<i class="fas fa-save me-1"></i> Guardar
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-lg-8">
|
|
<div class="card shadow-sm mb-4">
|
|
<div class="card-header bg-light d-flex justify-content-between align-items-center">
|
|
<h2 class="h5 mb-0">Colaboradores</h2>
|
|
<span class="badge bg-secondary">Total: <?= count($colaboradores); ?></span>
|
|
</div>
|
|
<div class="card-body">
|
|
<form method="GET" class="row g-2 align-items-end">
|
|
<div class="col-sm-4">
|
|
<label class="form-label">Buscar</label>
|
|
<input type="text" name="q" class="form-control" placeholder="Nombre, DNI o Cargo" value="<?= htmlspecialchars($search, ENT_QUOTES, 'UTF-8'); ?>">
|
|
</div>
|
|
<div class="col-sm-3">
|
|
<label class="form-label">Área</label>
|
|
<select name="area" class="form-select">
|
|
<option value="Todas">Todas</option>
|
|
<?php foreach ($areasValidas as $a): ?>
|
|
<option value="<?= htmlspecialchars($a, ENT_QUOTES, 'UTF-8'); ?>" <?= $filter_area === $a ? 'selected' : ''; ?>><?= htmlspecialchars($a, ENT_QUOTES, 'UTF-8'); ?></option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</div>
|
|
<div class="col-sm-3">
|
|
<label class="form-label">Estado</label>
|
|
<select name="estado" class="form-select">
|
|
<option value="Todos">Todos</option>
|
|
<?php foreach ($estadosValidos as $e): ?>
|
|
<option value="<?= htmlspecialchars($e, ENT_QUOTES, 'UTF-8'); ?>" <?= $filter_estado === $e ? 'selected' : ''; ?>><?= htmlspecialchars($e, ENT_QUOTES, 'UTF-8'); ?></option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</div>
|
|
<div class="col-12 d-flex justify-content-end gap-2">
|
|
<a class="btn btn-outline-secondary" href="hr_colaboradores.php">Limpiar</a>
|
|
<button class="btn btn-primary" type="submit">
|
|
<i class="fas fa-filter me-1"></i> Filtrar
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="table-responsive">
|
|
<table class="table table-bordered table-striped align-middle">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th>ID</th>
|
|
<th>Nombre Completo</th>
|
|
<th>DNI</th>
|
|
<th>Celular</th>
|
|
<th>Correo</th>
|
|
<th>Área</th>
|
|
<th>Cargo</th>
|
|
<th>Fecha Ingreso</th>
|
|
<th>Estado</th>
|
|
<th>Observaciones</th>
|
|
<th>Acciones</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php if (empty($colaboradores)): ?>
|
|
<tr>
|
|
<td colspan="11" class="text-center py-4 text-muted">No hay colaboradores para los filtros seleccionados.</td>
|
|
</tr>
|
|
<?php else: ?>
|
|
<?php foreach ($colaboradores as $c): ?>
|
|
<tr>
|
|
<td><?= (int)$c['id']; ?></td>
|
|
<td><?= htmlspecialchars($c['nombre_completo'] ?? '', ENT_QUOTES, 'UTF-8'); ?></td>
|
|
<td><?= htmlspecialchars($c['dni'] ?? '', ENT_QUOTES, 'UTF-8'); ?></td>
|
|
<td><?= htmlspecialchars($c['celular'] ?? '', ENT_QUOTES, 'UTF-8'); ?></td>
|
|
<td><?= htmlspecialchars($c['correo'] ?? '', ENT_QUOTES, 'UTF-8'); ?></td>
|
|
<td><span class="badge bg-primary"><?= htmlspecialchars($c['area'] ?? '', ENT_QUOTES, 'UTF-8'); ?></span></td>
|
|
<td><?= htmlspecialchars($c['cargo'] ?? '', ENT_QUOTES, 'UTF-8'); ?></td>
|
|
<td><?= htmlspecialchars($c['fecha_ingreso'] ?? '', ENT_QUOTES, 'UTF-8'); ?></td>
|
|
<td><span class="badge bg-info text-dark"><?= htmlspecialchars($c['estado'] ?? '', ENT_QUOTES, 'UTF-8'); ?></span></td>
|
|
<td style="max-width: 220px;">
|
|
<div style="white-space: pre-wrap;"><?= htmlspecialchars($c['observaciones'] ?? '', ENT_QUOTES, 'UTF-8'); ?></div>
|
|
</td>
|
|
<td>
|
|
<form method="POST" class="d-flex flex-column gap-1">
|
|
<input type="hidden" name="action" value="update_colaborador_estado">
|
|
<input type="hidden" name="id" value="<?= (int)$c['id']; ?>">
|
|
<select name="estado" class="form-select form-select-sm">
|
|
<?php foreach ($estadosValidos as $e): ?>
|
|
<option value="<?= htmlspecialchars($e, ENT_QUOTES, 'UTF-8'); ?>" <?= ($c['estado'] ?? '') === $e ? 'selected' : ''; ?>>
|
|
<?= htmlspecialchars($e, ENT_QUOTES, 'UTF-8'); ?>
|
|
</option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
<button type="submit" class="btn btn-sm btn-success">
|
|
<i class="fas fa-save me-1"></i> Guardar
|
|
</button>
|
|
</form>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
<?php endif; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<?php include 'layout_footer.php'; ?>
|