Files
gestoria-lp/admin/recordatorios.php
Gestoría LP 3e12204828 feat: admin reminders module
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 00:36:10 +00:00

390 lines
19 KiB
PHP

<?php
$pageTitle = 'Recordatorios';
require_once __DIR__ . '/../includes/db.php';
require_once __DIR__ . '/includes/admin-header.php';
$db = getDB();
$errors = [];
$success = '';
$today = date('Y-m-d');
// ── Handle POST actions ─────────────────────────────────────────
if ($_SERVER['REQUEST_METHOD'] === 'POST' && csrfValidate()) {
$action = $_POST['action'] ?? '';
switch ($action) {
// ── Add reminder ──────────────────────────────────────────
case 'add_reminder':
$titulo = trim($_POST['titulo'] ?? '');
$descripcion = trim($_POST['descripcion'] ?? '');
$fecha = trim($_POST['fecha'] ?? '');
$clienteId = (int)($_POST['cliente_id'] ?? 0);
$tramiteId = (int)($_POST['tramite_id'] ?? 0);
if ($titulo === '') {
$errors[] = 'El t&iacute;tulo es obligatorio.';
}
if ($fecha === '' || !preg_match('/^\d{4}-\d{2}-\d{2}$/', $fecha)) {
$errors[] = 'La fecha es obligatoria y debe ser v&aacute;lida.';
}
if (empty($errors)) {
$stmt = $db->prepare("INSERT INTO recordatorios
(cliente_id, tramite_id, titulo, descripcion, fecha, completado, created_at)
VALUES (?, ?, ?, ?, ?, 0, NOW())");
$stmt->execute([
$clienteId > 0 ? $clienteId : null,
$tramiteId > 0 ? $tramiteId : null,
$titulo,
$descripcion,
$fecha,
]);
header('Location: recordatorios.php?added=1' . (!empty($_GET['show_completed']) ? '&show_completed=1' : ''));
exit;
}
break;
// ── Complete reminder ─────────────────────────────────────
case 'complete_reminder':
$id = (int)($_POST['reminder_id'] ?? 0);
if ($id > 0) {
$stmt = $db->prepare("UPDATE recordatorios SET completado = 1 WHERE id = ?");
$stmt->execute([$id]);
}
header('Location: recordatorios.php?completed=1' . (!empty($_GET['show_completed']) ? '&show_completed=1' : ''));
exit;
// ── Delete reminder ───────────────────────────────────────
case 'delete_reminder':
$id = (int)($_POST['reminder_id'] ?? 0);
if ($id > 0) {
$stmt = $db->prepare("DELETE FROM recordatorios WHERE id = ?");
$stmt->execute([$id]);
}
header('Location: recordatorios.php?deleted=1' . (!empty($_GET['show_completed']) ? '&show_completed=1' : ''));
exit;
}
}
// ── Flash messages from redirect ────────────────────────────────
if (isset($_GET['added'])) $success = 'Recordatorio creado correctamente.';
if (isset($_GET['completed'])) $success = 'Recordatorio marcado como completado.';
if (isset($_GET['deleted'])) $success = 'Recordatorio eliminado correctamente.';
// ── Filters & pagination ────────────────────────────────────────
$showCompleted = isset($_GET['show_completed']) && $_GET['show_completed'] === '1';
$page = max(1, (int)($_GET['page'] ?? 1));
$perPage = 20;
$offset = ($page - 1) * $perPage;
// Build WHERE clause
$where = '';
$params = [];
if (!$showCompleted) {
$where = 'WHERE r.completado = 0';
}
// Count total
$countSql = "SELECT COUNT(*) FROM recordatorios r $where";
$countStmt = $db->prepare($countSql);
$countStmt->execute($params);
$totalRecordatorios = $countStmt->fetchColumn();
$totalPages = max(1, (int)ceil($totalRecordatorios / $perPage));
if ($page > $totalPages) $page = $totalPages;
$offset = ($page - 1) * $perPage;
// Fetch reminders with client name
$sql = "SELECT r.*, c.nombre AS cliente_nombre
FROM recordatorios r
LEFT JOIN clientes c ON r.cliente_id = c.id
$where
ORDER BY r.fecha ASC
LIMIT $perPage OFFSET $offset";
$stmt = $db->prepare($sql);
$stmt->execute($params);
$recordatorios = $stmt->fetchAll();
// Count overdue (for badge in header)
$stmtOverdue = $db->prepare("SELECT COUNT(*) FROM recordatorios WHERE completado = 0 AND fecha < ?");
$stmtOverdue->execute([$today]);
$countOverdue = (int)$stmtOverdue->fetchColumn();
// Fetch clients for dropdown
$clientesStmt = $db->query("SELECT id, nombre FROM clientes ORDER BY nombre ASC");
$clientesList = $clientesStmt->fetchAll();
// Fetch tramites for dropdown
$tramitesStmt = $db->query("SELECT t.id, t.tipo, c.nombre AS cliente_nombre
FROM tramites t
LEFT JOIN clientes c ON t.cliente_id = c.id
ORDER BY t.created_at DESC LIMIT 100");
$tramitesList = $tramitesStmt->fetchAll();
$tipoLabels = [
'visa' => 'Visa',
'sentri' => 'Sentri/Global',
'pasaporte' => 'Pasaporte',
'adelanto_cita' => 'Adelanto Cita',
'doble_nacionalidad' => 'Doble Nacionalidad',
];
?>
<div class="admin-content__header">
<h1>
<i class="fas fa-bell"></i> Recordatorios
<?php if ($countOverdue > 0): ?>
<span class="badge badge--danger" style="font-size: 0.5em; vertical-align: middle;">
<?= $countOverdue ?> vencido<?= $countOverdue > 1 ? 's' : '' ?>
</span>
<?php endif; ?>
</h1>
<p>Gestiona tus recordatorios y tareas pendientes</p>
</div>
<!-- Success / Error messages -->
<?php if ($success !== ''): ?>
<div class="alert alert--success" style="padding: 0.75rem 1rem; margin-bottom: 1rem; border-radius: 0.5rem; background: var(--admin-success-light); color: #155724; border: 1px solid #C3E6CB;">
<i class="fas fa-check-circle"></i> <?= $success ?>
</div>
<?php endif; ?>
<?php if (!empty($errors)): ?>
<div class="alert alert--danger" style="padding: 0.75rem 1rem; margin-bottom: 1rem; border-radius: 0.5rem; background: var(--admin-danger-light); color: #721C24; border: 1px solid #F5C6CB;">
<i class="fas fa-exclamation-circle"></i>
<ul style="margin: 0.25rem 0 0 1.25rem; padding: 0;">
<?php foreach ($errors as $e): ?>
<li><?= $e ?></li>
<?php endforeach; ?>
</ul>
</div>
<?php endif; ?>
<!-- Add Reminder Form -->
<div class="card" style="margin-bottom: 1.5rem;">
<div class="card__header" style="padding: 1rem 1.25rem; border-bottom: 1px solid var(--admin-gray-200); font-weight: 600;">
<i class="fas fa-plus-circle"></i> Nuevo Recordatorio
</div>
<div class="card__body">
<form method="POST" action="recordatorios.php<?= $showCompleted ? '?show_completed=1' : '' ?>">
<?= csrfField() ?>
<input type="hidden" name="action" value="add_reminder">
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 1rem; margin-bottom: 1rem;">
<div class="form-group" style="margin-bottom: 0;">
<label>T&iacute;tulo <span class="required">*</span></label>
<input type="text" name="titulo" class="form-control" required
value="<?= htmlspecialchars($_POST['titulo'] ?? '') ?>"
placeholder="Ej: Llamar al cliente...">
</div>
<div class="form-group" style="margin-bottom: 0;">
<label>Fecha <span class="required">*</span></label>
<input type="date" name="fecha" class="form-control" required
value="<?= htmlspecialchars($_POST['fecha'] ?? $today) ?>">
</div>
<div class="form-group" style="margin-bottom: 0;">
<label>Cliente <small>(opcional)</small></label>
<select name="cliente_id" class="form-control">
<option value="0">-- Ninguno --</option>
<?php foreach ($clientesList as $cl): ?>
<option value="<?= (int)$cl['id'] ?>" <?= ((int)($_POST['cliente_id'] ?? 0) === (int)$cl['id']) ? 'selected' : '' ?>>
<?= htmlspecialchars($cl['nombre']) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group" style="margin-bottom: 0;">
<label>Tr&aacute;mite <small>(opcional)</small></label>
<select name="tramite_id" class="form-control">
<option value="0">-- Ninguno --</option>
<?php foreach ($tramitesList as $tr): ?>
<option value="<?= (int)$tr['id'] ?>" <?= ((int)($_POST['tramite_id'] ?? 0) === (int)$tr['id']) ? 'selected' : '' ?>>
#<?= (int)$tr['id'] ?> - <?= htmlspecialchars($tipoLabels[$tr['tipo']] ?? $tr['tipo']) ?> (<?= htmlspecialchars($tr['cliente_nombre'] ?? 'Sin cliente') ?>)
</option>
<?php endforeach; ?>
</select>
</div>
</div>
<div class="form-group" style="margin-bottom: 1rem;">
<label>Descripci&oacute;n <small>(opcional)</small></label>
<textarea name="descripcion" class="form-control" rows="2"
placeholder="Detalles adicionales del recordatorio..."><?= htmlspecialchars($_POST['descripcion'] ?? '') ?></textarea>
</div>
<button type="submit" class="btn btn--primary">
<i class="fas fa-plus"></i> Agregar Recordatorio
</button>
</form>
</div>
</div>
<!-- Toolbar: Filter toggle -->
<div class="toolbar">
<div class="toolbar__left">
<?php if ($showCompleted): ?>
<a href="recordatorios.php" class="btn btn--sm btn--secondary">
<i class="fas fa-eye-slash"></i> Ocultar completados
</a>
<?php else: ?>
<a href="recordatorios.php?show_completed=1" class="btn btn--sm btn--secondary">
<i class="fas fa-eye"></i> Mostrar todos
</a>
<?php endif; ?>
<span style="margin-left: 0.75rem; color: var(--admin-gray-600); font-size: var(--admin-font-sm);">
<?= $totalRecordatorios ?> recordatorio<?= $totalRecordatorios !== 1 ? 's' : '' ?>
<?= !$showCompleted ? ' pendiente' . ($totalRecordatorios !== 1 ? 's' : '') : ' en total' ?>
</span>
</div>
</div>
<!-- Reminders List -->
<div class="card">
<div class="card__body">
<?php if (empty($recordatorios)): ?>
<div class="table-empty">
<i class="fas fa-bell"></i>
<p><?= !$showCompleted ? 'No hay recordatorios pendientes.' : 'No hay recordatorios registrados a&uacute;n.' ?></p>
</div>
<?php else: ?>
<div class="table-responsive">
<table class="admin-table">
<thead>
<tr>
<th style="width: 110px;">Fecha</th>
<th>T&iacute;tulo</th>
<th>Descripci&oacute;n</th>
<th>Cliente</th>
<?php if ($showCompleted): ?>
<th>Estado</th>
<?php endif; ?>
<th style="width: 180px;">Acciones</th>
</tr>
</thead>
<tbody>
<?php foreach ($recordatorios as $r):
// Determine color coding
$fechaR = $r['fecha'];
$isCompleted = (int)$r['completado'] === 1;
if ($isCompleted) {
$rowStyle = 'opacity: 0.6;';
$dateClass = 'secondary';
} elseif ($fechaR < $today) {
$rowStyle = 'background: rgba(220, 53, 69, 0.06);';
$dateClass = 'danger';
} elseif ($fechaR === $today) {
$rowStyle = 'background: rgba(255, 193, 7, 0.1);';
$dateClass = 'warning';
} else {
$rowStyle = '';
$dateClass = 'success';
}
?>
<tr style="<?= $rowStyle ?>">
<td>
<span class="badge badge--<?= $dateClass ?>">
<?= date('d/m/Y', strtotime($fechaR)) ?>
</span>
</td>
<td>
<strong><?= htmlspecialchars($r['titulo']) ?></strong>
</td>
<td>
<?php
$desc = $r['descripcion'] ?? '';
echo htmlspecialchars(mb_strlen($desc) > 80 ? mb_substr($desc, 0, 80) . '...' : $desc);
?>
</td>
<td>
<?php if ($r['cliente_id'] && $r['cliente_nombre']): ?>
<a href="cliente-detalle.php?id=<?= (int)$r['cliente_id'] ?>" style="color: var(--admin-primary); text-decoration: none;">
<?= htmlspecialchars($r['cliente_nombre']) ?>
</a>
<?php else: ?>
<span style="color: var(--admin-gray-500);">-</span>
<?php endif; ?>
</td>
<?php if ($showCompleted): ?>
<td>
<?php if ($isCompleted): ?>
<span class="badge badge--success"><i class="fas fa-check"></i> Completado</span>
<?php else: ?>
<span class="badge badge--primary">Pendiente</span>
<?php endif; ?>
</td>
<?php endif; ?>
<td>
<div style="display: flex; gap: 0.5rem;">
<?php if (!$isCompleted): ?>
<form method="POST" action="recordatorios.php<?= $showCompleted ? '?show_completed=1' : '' ?>" style="display: inline;">
<?= csrfField() ?>
<input type="hidden" name="action" value="complete_reminder">
<input type="hidden" name="reminder_id" value="<?= (int)$r['id'] ?>">
<button type="submit" class="btn btn--sm btn--success" title="Marcar como completado">
<i class="fas fa-check"></i>
</button>
</form>
<?php endif; ?>
<form method="POST" action="recordatorios.php<?= $showCompleted ? '?show_completed=1' : '' ?>" style="display: inline;"
onsubmit="return confirm('&iquest;Eliminar este recordatorio?');">
<?= csrfField() ?>
<input type="hidden" name="action" value="delete_reminder">
<input type="hidden" name="reminder_id" value="<?= (int)$r['id'] ?>">
<button type="submit" class="btn btn--sm btn--danger" title="Eliminar">
<i class="fas fa-trash"></i>
</button>
</form>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<!-- Pagination -->
<?php if ($totalPages > 1): ?>
<div class="pagination">
<?php
$queryParams = '';
if ($showCompleted) $queryParams .= '&show_completed=1';
?>
<a href="?page=1<?= $queryParams ?>" class="pagination__link <?= $page <= 1 ? 'pagination__link--disabled' : '' ?>">
<i class="fas fa-angle-double-left"></i>
</a>
<a href="?page=<?= $page - 1 ?><?= $queryParams ?>" class="pagination__link <?= $page <= 1 ? 'pagination__link--disabled' : '' ?>">
<i class="fas fa-angle-left"></i>
</a>
<?php
$start = max(1, $page - 2);
$end = min($totalPages, $page + 2);
for ($i = $start; $i <= $end; $i++):
?>
<a href="?page=<?= $i ?><?= $queryParams ?>" class="pagination__link <?= $i === $page ? 'pagination__link--active' : '' ?>">
<?= $i ?>
</a>
<?php endfor; ?>
<a href="?page=<?= $page + 1 ?><?= $queryParams ?>" class="pagination__link <?= $page >= $totalPages ? 'pagination__link--disabled' : '' ?>">
<i class="fas fa-angle-right"></i>
</a>
<a href="?page=<?= $totalPages ?><?= $queryParams ?>" class="pagination__link <?= $page >= $totalPages ? 'pagination__link--disabled' : '' ?>">
<i class="fas fa-angle-double-right"></i>
</a>
<span class="pagination__info">
P&aacute;gina <?= $page ?> de <?= $totalPages ?> (<?= $totalRecordatorios ?> recordatorios)
</span>
</div>
<?php endif; ?>
<?php endif; ?>
</div>
</div>
<?php require_once __DIR__ . '/includes/admin-footer.php'; ?>