feat: admin requests inbox with conversion to clients

Add solicitudes list page with estado filtering, pagination, and new
request count badge. Add detail page showing all form data with
actions to change status, create new client, link to existing client,
and create tramite from linked client.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Gestoría LP
2026-03-02 00:33:31 +00:00
parent b78b18b65a
commit 676e8981b8
2 changed files with 617 additions and 0 deletions

213
admin/solicitudes.php Normal file
View File

@@ -0,0 +1,213 @@
<?php
$pageTitle = 'Solicitudes';
require_once __DIR__ . '/../includes/db.php';
require_once __DIR__ . '/includes/admin-header.php';
$db = getDB();
// Label maps
$servicioLabels = [
'visa' => 'Visa',
'sentri' => 'Sentri/Global',
'pasaporte' => 'Pasaporte',
'adelanto_cita' => 'Adelanto Cita',
'doble_nacionalidad' => 'Doble Nacionalidad',
];
$estadoSolicitudLabels = [
'nueva' => 'Nueva',
'contactada' => 'Contactada',
'convertida' => 'Convertida',
'descartada' => 'Descartada',
];
$estadoBadgeClass = [
'nueva' => 'primary',
'contactada' => 'warning',
'convertida' => 'success',
'descartada' => 'danger',
];
// Filters & Pagination
$fEstado = trim($_GET['estado'] ?? '');
$page = max(1, (int)($_GET['page'] ?? 1));
$perPage = 15;
$offset = ($page - 1) * $perPage;
// Validate filter
$validEstados = array_keys($estadoSolicitudLabels);
if ($fEstado !== '' && !in_array($fEstado, $validEstados, true)) $fEstado = '';
// Build WHERE clause
$conditions = [];
$params = [];
if ($fEstado !== '') {
$conditions[] = 's.estado = ?';
$params[] = $fEstado;
}
$where = '';
if (!empty($conditions)) {
$where = 'WHERE ' . implode(' AND ', $conditions);
}
// Count total
$countSql = "SELECT COUNT(*) FROM solicitudes s $where";
$countStmt = $db->prepare($countSql);
$countStmt->execute($params);
$totalSolicitudes = $countStmt->fetchColumn();
$totalPages = max(1, (int)ceil($totalSolicitudes / $perPage));
// Ensure page is within range
if ($page > $totalPages) $page = $totalPages;
// Count new requests for header badge
$stmtNew = $db->prepare("SELECT COUNT(*) FROM solicitudes WHERE estado = 'nueva'");
$stmtNew->execute();
$countNuevas = (int)$stmtNew->fetchColumn();
// Fetch solicitudes
$sql = "SELECT s.*
FROM solicitudes s
$where
ORDER BY s.created_at DESC
LIMIT $perPage OFFSET $offset";
$stmt = $db->prepare($sql);
$stmt->execute($params);
$solicitudes = $stmt->fetchAll();
// Check if any filters are active
$hasFilters = ($fEstado !== '');
?>
<div class="admin-content__header">
<h1>
<i class="fas fa-inbox"></i> Solicitudes
<?php if ($countNuevas > 0): ?>
<span class="badge badge--primary" style="font-size: 0.5em; vertical-align: middle;">
<?= $countNuevas ?> nueva<?= $countNuevas > 1 ? 's' : '' ?>
</span>
<?php endif; ?>
</h1>
<p>Solicitudes recibidas desde el sitio web</p>
</div>
<!-- Toolbar: Filters -->
<div class="toolbar">
<div class="toolbar__left">
<form method="GET" class="search-bar search-bar--wide" action="solicitudes.php" style="display: flex; gap: 0.75rem; align-items: center; flex-wrap: wrap;">
<select name="estado" class="form-control" style="width: auto; min-width: 160px;">
<option value="">-- Todos los estados --</option>
<?php foreach ($estadoSolicitudLabels as $val => $label): ?>
<option value="<?= htmlspecialchars($val) ?>" <?= $fEstado === $val ? 'selected' : '' ?>>
<?= htmlspecialchars($label) ?>
</option>
<?php endforeach; ?>
</select>
<button type="submit" class="btn btn--sm btn--secondary">
<i class="fas fa-filter"></i> Filtrar
</button>
</form>
<?php if ($hasFilters): ?>
<a href="solicitudes.php" class="btn btn--sm btn--secondary" style="margin-left: 0.5rem;">
<i class="fas fa-times"></i> Limpiar
</a>
<?php endif; ?>
</div>
</div>
<!-- Solicitudes Table -->
<div class="card">
<div class="card__body">
<?php if (empty($solicitudes)): ?>
<div class="table-empty">
<i class="fas fa-inbox"></i>
<p><?= $hasFilters ? 'No se encontraron solicitudes con ese filtro.' : 'No hay solicitudes registradas a&uacute;n.' ?></p>
<?php if ($hasFilters): ?>
<a href="solicitudes.php" class="btn btn--sm btn--outline">Ver todas</a>
<?php endif; ?>
</div>
<?php else: ?>
<div class="table-responsive">
<table class="admin-table">
<thead>
<tr>
<th>Nombre</th>
<th>Servicio</th>
<th>Tel&eacute;fono</th>
<th>Email</th>
<th>Estado</th>
<th>Fecha</th>
<th>Acci&oacute;n</th>
</tr>
</thead>
<tbody>
<?php foreach ($solicitudes as $s): ?>
<tr<?= $s['estado'] === 'nueva' ? ' style="background: rgba(23, 162, 184, 0.06);"' : '' ?>>
<td>
<?php if ($s['estado'] === 'nueva'): ?>
<strong style="color: var(--admin-primary, #0C7C8C);"><?= htmlspecialchars($s['nombre']) ?></strong>
<?php else: ?>
<strong><?= htmlspecialchars($s['nombre']) ?></strong>
<?php endif; ?>
</td>
<td><?= htmlspecialchars($servicioLabels[$s['servicio']] ?? $s['servicio']) ?></td>
<td><?= htmlspecialchars($s['telefono'] ?: '-') ?></td>
<td><?= htmlspecialchars($s['email'] ?: '-') ?></td>
<td>
<span class="badge badge--<?= htmlspecialchars($estadoBadgeClass[$s['estado']] ?? 'secondary') ?>">
<?= htmlspecialchars($estadoSolicitudLabels[$s['estado']] ?? $s['estado']) ?>
</span>
</td>
<td><?= date('d/m/Y H:i', strtotime($s['created_at'])) ?></td>
<td>
<a href="solicitud-detalle.php?id=<?= (int)$s['id'] ?>" class="btn btn--sm btn--primary">
<i class="fas fa-eye"></i> Ver
</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<!-- Pagination -->
<?php if ($totalPages > 1): ?>
<div class="pagination">
<?php
$queryParams = '';
if ($fEstado !== '') $queryParams .= '&estado=' . urlencode($fEstado);
?>
<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 ?> (<?= $totalSolicitudes ?> solicitudes)
</span>
</div>
<?php endif; ?>
<?php endif; ?>
</div>
</div>
<?php require_once __DIR__ . '/includes/admin-footer.php'; ?>