40097-vm/gestiones_callcenter.php
2026-05-25 01:22:01 +00:00

476 lines
32 KiB
PHP

<?php
session_start();
require_once 'db/config.php';
require_once 'includes/drive_test_orders.php';
$pageTitle = 'Call Center de Prueba | Importar Drive (Test)';
$pageDescription = 'Panel de seguimiento para confirmar pedidos usando únicamente los últimos 10 registros de Importar Drive (Test).';
$view = $_GET['view'] ?? 'pendientes';
$allowedViews = [
'pendientes' => ['estado' => 'POR LLAMAR', 'titulo' => 'Pedidos por llamar'],
'reintentos' => ['estado' => 'REINTENTO', 'titulo' => 'Pedidos en reintento'],
'confirmados' => ['estado' => 'CONFIRMADO', 'titulo' => 'Pedidos confirmados'],
'todos' => ['estado' => null, 'titulo' => 'Todos los pedidos de prueba'],
];
if (!isset($allowedViews[$view])) {
$view = 'pendientes';
}
$errorMessage = null;
$orders = [];
$stats = [
'total' => 0,
'pendientes' => 0,
'reintentos' => 0,
'confirmados' => 0,
];
$visibleOrders = [];
$totalRows = 0;
try {
$preview = drive_test_fetch_orders(10);
$totalRows = (int) ($preview['total_rows'] ?? 0);
$orders = $preview['orders'] ?? [];
$tracking = drive_test_fetch_tracking(db(), array_column($orders, 'source_key'));
$orders = drive_test_merge_tracking($orders, $tracking);
// Obtener conteo de llamadas
$sourceKeys = array_column($orders, 'source_key');
$callCounts = [];
if (!empty($sourceKeys)) {
$placeholders = implode(',', array_fill(0, count($sourceKeys), '?'));
$stmtCalls = db()->prepare("SELECT pedido_id, COUNT(*) as total FROM historial_llamadas WHERE pedido_id IN ($placeholders) GROUP BY pedido_id");
$stmtCalls->execute($sourceKeys);
$callCounts = $stmtCalls->fetchAll(PDO::FETCH_KEY_PAIR);
}
foreach ($orders as &$order) {
$order['total_llamadas'] = $callCounts[$order['source_key']] ?? 0;
$stats['total']++;
if ($order['estado'] === 'POR LLAMAR') {
$stats['pendientes']++;
} elseif ($order['estado'] === 'REINTENTO') {
$stats['reintentos']++;
} elseif ($order['estado'] === 'CONFIRMADO') {
$stats['confirmados']++;
}
}
$expectedState = $allowedViews[$view]['estado'];
if ($expectedState === null) {
$visibleOrders = $orders;
} else {
$visibleOrders = array_values(array_filter($orders, static function (array $order) use ($expectedState): bool {
return ($order['estado'] ?? '') === $expectedState;
}));
}
} catch (Throwable $exception) {
$errorMessage = $exception->getMessage();
}
function cc_test_badge_class(string $estado): string
{
return match ($estado) {
'CONFIRMADO' => 'bg-success-subtle text-success-emphasis',
'REINTENTO' => 'bg-info-subtle text-info-emphasis',
default => 'bg-warning-subtle text-warning-emphasis',
};
}
function cc_test_display_value(?string $value, string $fallback = 'No registrado'): string
{
$value = trim((string) $value);
return $value !== '' ? $value : $fallback;
}
function cc_test_format_price(?string $value): string
{
$value = trim((string) $value);
return $value !== '' ? 'S/ ' . $value : 'No registrado';
}
function cc_test_format_drive_datetime(?string $value): string
{
$value = trim((string) $value);
if ($value === '') {
return 'No registrada';
}
try {
return (new DateTimeImmutable($value))->format('d/m/Y h:i A');
} catch (Throwable $exception) {
return $value;
}
}
require_once 'layout_header.php';
?>
<main class="container-fluid py-4">
<section class="mb-4">
<div class="d-flex flex-column flex-lg-row justify-content-between align-items-lg-center gap-3">
<div>
<h1 class="h2 fw-bold mb-1"><i class="bi bi-headset text-primary"></i> Call Center de prueba</h1>
<p class="text-muted mb-0">Este panel usa solo los últimos 10 registros de <strong>Importar Drive (Test)</strong> para confirmar pedidos sin tocar pedidos reales.</p>
</div>
<div class="d-flex flex-wrap gap-2">
<span class="badge rounded-pill text-bg-light border px-3 py-2">Fuente: Importar Drive (Test)</span>
<span class="badge rounded-pill text-bg-light border px-3 py-2">Hoja detectada: <?php echo (int) $totalRows; ?> registros</span>
<a href="test_importar_drive.php" class="btn btn-outline-primary btn-sm">Ver vista previa</a>
</div>
</div>
</section>
<?php if ($errorMessage !== null): ?>
<section class="alert alert-danger" role="alert">
<strong>No se pudo cargar el panel de Call Center de prueba.</strong><br>
<?php echo htmlspecialchars($errorMessage); ?>
</section>
<?php else: ?>
<section class="row g-3 mb-4">
<div class="col-md-6 col-xl-3">
<a href="?view=todos" class="text-decoration-none">
<article class="card border-0 shadow-sm h-100 <?php echo $view === 'todos' ? 'bg-dark text-white' : 'bg-white'; ?>">
<div class="card-body">
<div class="small text-uppercase opacity-75 mb-2">Total visibles</div>
<div class="display-6 fw-bold mb-0"><?php echo (int) $stats['total']; ?></div>
</div>
</article>
</a>
</div>
<div class="col-md-6 col-xl-3">
<a href="?view=pendientes" class="text-decoration-none">
<article class="card border-0 shadow-sm h-100 <?php echo $view === 'pendientes' ? 'bg-warning-subtle border border-warning' : 'bg-white'; ?>">
<div class="card-body">
<div class="small text-uppercase text-muted mb-2">Por llamar</div>
<div class="display-6 fw-bold mb-0"><?php echo (int) $stats['pendientes']; ?></div>
</div>
</article>
</a>
</div>
<div class="col-md-6 col-xl-3">
<a href="?view=reintentos" class="text-decoration-none">
<article class="card border-0 shadow-sm h-100 <?php echo $view === 'reintentos' ? 'bg-info-subtle border border-info' : 'bg-white'; ?>">
<div class="card-body">
<div class="small text-uppercase text-muted mb-2">Reintento</div>
<div class="display-6 fw-bold mb-0"><?php echo (int) $stats['reintentos']; ?></div>
</div>
</article>
</a>
</div>
<div class="col-md-6 col-xl-3">
<a href="?view=confirmados" class="text-decoration-none">
<article class="card border-0 shadow-sm h-100 <?php echo $view === 'confirmados' ? 'bg-success-subtle border border-success' : 'bg-white'; ?>">
<div class="card-body">
<div class="small text-uppercase text-muted mb-2">Confirmados</div>
<div class="display-6 fw-bold mb-0"><?php echo (int) $stats['confirmados']; ?></div>
</div>
</article>
</a>
</div>
</section>
<section class="card border-0 shadow-sm">
<div class="card-header bg-white py-3 d-flex flex-column flex-lg-row justify-content-between align-items-lg-center gap-2">
<div>
<h2 class="h5 fw-bold mb-1"><?php echo htmlspecialchars($allowedViews[$view]['titulo']); ?></h2>
<p class="text-muted small mb-0">Datos tomados solo desde <strong>Importar Drive (Test)</strong>: nombre, celular, dirección, referencia, distrito, ciudad, producto, cantidad, precio, método, sede / ID, DNI y observaciones.</p>
</div>
<span class="badge bg-light text-dark border"><?php echo count($visibleOrders); ?> pedidos</span>
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead class="table-light">
<tr>
<th>Cliente y contacto</th>
<th>Ubicación</th>
<th>Pedido</th>
<th>Seguimiento</th>
<th class="text-center">Acciones</th>
</tr>
</thead>
<tbody>
<?php if (empty($visibleOrders)): ?>
<tr>
<td colspan="5" class="text-center py-5 text-muted">
<i class="bi bi-inbox fs-2 d-block mb-2"></i>
No hay pedidos en esta vista del módulo de prueba.
</td>
</tr>
<?php else: ?>
<?php foreach ($visibleOrders as $order): ?>
<?php
$modalId = 'modalDriveTest' . substr($order['source_key'], 0, 10);
$badgeClass = cc_test_badge_class($order['estado']);
$precioLabel = cc_test_format_price($order['precio'] ?? '');
$driveDateLabel = cc_test_format_drive_datetime($order['import_id'] ?? '');
?>
<tr>
<td>
<div class="fw-bold"><?php echo htmlspecialchars(cc_test_display_value($order['nombre'])); ?></div>
<div class="small text-muted">Pedido: <?php echo htmlspecialchars(cc_test_display_value($order['codigo'], 'Sin código')); ?></div>
<div class="small text-muted">Drive: <?php echo htmlspecialchars($driveDateLabel); ?></div>
<div class="small text-muted">DNI: <?php echo htmlspecialchars(cc_test_display_value($order['dni'])); ?></div>
<?php if (!empty($order['telefono_url'])): ?>
<a href="<?php echo htmlspecialchars($order['telefono_url']); ?>" class="text-decoration-none small">
<i class="bi bi-telephone-fill"></i> <?php echo htmlspecialchars(cc_test_display_value($order['celular'])); ?>
</a>
<?php else: ?>
<div class="small text-muted">Celular: No registrado</div>
<?php endif; ?>
</td>
<td>
<div class="fw-semibold text-primary"><?php echo htmlspecialchars(cc_test_display_value($order['distrito'])); ?></div>
<div class="small"><?php echo htmlspecialchars(cc_test_display_value($order['direccion'])); ?></div>
<div class="small text-muted">Ref.: <?php echo htmlspecialchars(cc_test_display_value($order['referencia'])); ?></div>
<div class="small text-muted"><?php echo htmlspecialchars(cc_test_display_value($order['ciudad'])); ?> · <?php echo htmlspecialchars(cc_test_display_value($order['pais'])); ?></div>
<?php if (!empty($order['coordenadas'])): ?>
<a href="<?php echo htmlspecialchars($order['coordenadas']); ?>" target="_blank" rel="noopener noreferrer" class="btn btn-sm btn-link px-0 text-decoration-none">Ver ubicación</a>
<?php endif; ?>
</td>
<td>
<div class="small fw-semibold"><?php echo nl2br(htmlspecialchars(cc_test_display_value($order['producto']))); ?></div>
<div class="small text-muted">Cantidad: <?php echo htmlspecialchars(cc_test_display_value($order['cantidad'], '1')); ?> · Precio: <span class="text-success fw-bold"><?php echo htmlspecialchars($precioLabel); ?></span></div>
<div class="small text-muted">Método: <?php echo htmlspecialchars(cc_test_display_value($order['metodo'])); ?></div>
<div class="small text-muted">Sede / ID: <?php echo htmlspecialchars(cc_test_display_value($order['sede'])); ?></div>
</td>
<td>
<span class="badge <?php echo $badgeClass; ?> mb-2"><?php echo htmlspecialchars($order['estado']); ?></span>
<?php if ($order['total_llamadas'] > 0): ?>
<div class="mb-2">
<span class="badge bg-secondary" title="Intentos de llamada">
<i class="bi bi-telephone-outbound"></i> <?php echo (int) $order['total_llamadas']; ?> llamadas
</span>
</div>
<?php endif; ?>
<div class="small text-muted">Obs. Drive: <?php echo htmlspecialchars(cc_test_display_value($order['observaciones'], 'Sin observaciones')); ?></div>
<div class="small text-muted">Nota interna: <?php echo htmlspecialchars(cc_test_display_value($order['nota_seguimiento'], 'Sin nota')); ?></div>
</td>
<td class="text-center">
<div class="d-flex flex-column flex-md-row justify-content-center gap-2">
<button type="button" class="btn btn-sm btn-outline-primary" data-bs-toggle="modal" data-bs-target="#<?php echo htmlspecialchars($modalId); ?>">
<i class="bi bi-eye"></i> Ver
</button>
<?php if (!empty($order['telefono_url'])): ?>
<a href="<?php echo htmlspecialchars($order['telefono_url']); ?>"
class="btn btn-sm btn-outline-secondary"
onclick="registrarLlamada('<?php echo htmlspecialchars($order['source_key']); ?>')">
<i class="bi bi-telephone"></i> Llamar
</a>
<?php endif; ?>
<?php if (!empty($order['whatsapp_url'])): ?>
<a href="<?php echo htmlspecialchars($order['whatsapp_url']); ?>" target="_blank" rel="noopener noreferrer" class="btn btn-sm btn-outline-success">
<i class="bi bi-whatsapp"></i> WhatsApp
</a>
<?php endif; ?>
</div>
</td>
</tr>
<div class="modal fade" id="<?php echo htmlspecialchars($modalId); ?>" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-lg modal-dialog-scrollable">
<div class="modal-content border-0 shadow">
<div class="modal-header">
<div>
<h3 class="h5 mb-1"><?php echo htmlspecialchars(cc_test_display_value($order['nombre'])); ?></h3>
<div class="small text-muted">Pedido de prueba cargado desde Importar Drive (Test)</div>
</div>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Cerrar"></button>
</div>
<div class="modal-body">
<div class="row g-3 mb-3">
<div class="col-md-4">
<label class="text-muted small d-block">Código</label>
<div class="fw-bold"><?php echo htmlspecialchars(cc_test_display_value($order['codigo'], 'Sin código')); ?></div>
</div>
<div class="col-md-4">
<label class="text-muted small d-block">Fecha detectada en Drive</label>
<div class="fw-bold"><?php echo htmlspecialchars($driveDateLabel); ?></div>
</div>
<div class="col-md-4">
<label class="text-muted small d-block">Estado de seguimiento</label>
<div><span class="badge <?php echo $badgeClass; ?>"><?php echo htmlspecialchars($order['estado']); ?></span></div>
</div>
<div class="col-md-6">
<label class="text-muted small d-block">Celular</label>
<div class="fw-bold"><?php echo htmlspecialchars(cc_test_display_value($order['celular'])); ?></div>
</div>
<div class="col-md-6">
<label class="text-muted small d-block">DNI</label>
<div class="fw-bold"><?php echo htmlspecialchars(cc_test_display_value($order['dni'])); ?></div>
</div>
<div class="col-md-12">
<label class="text-muted small d-block">Dirección</label>
<div class="fw-bold"><?php echo htmlspecialchars(cc_test_display_value($order['direccion'])); ?></div>
</div>
<div class="col-md-6">
<label class="text-muted small d-block">Referencia</label>
<div class="fw-bold"><?php echo htmlspecialchars(cc_test_display_value($order['referencia'])); ?></div>
</div>
<div class="col-md-3">
<label class="text-muted small d-block">Distrito</label>
<div class="fw-bold"><?php echo htmlspecialchars(cc_test_display_value($order['distrito'])); ?></div>
</div>
<div class="col-md-3">
<label class="text-muted small d-block">Ciudad</label>
<div class="fw-bold"><?php echo htmlspecialchars(cc_test_display_value($order['ciudad'])); ?></div>
</div>
<div class="col-md-4">
<label class="text-muted small d-block">País</label>
<div class="fw-bold"><?php echo htmlspecialchars(cc_test_display_value($order['pais'])); ?></div>
</div>
<div class="col-md-4">
<label class="text-muted small d-block">Método</label>
<div class="fw-bold"><?php echo htmlspecialchars(cc_test_display_value($order['metodo'])); ?></div>
</div>
<div class="col-md-4">
<label class="text-muted small d-block">Sede / ID</label>
<div class="fw-bold"><?php echo htmlspecialchars(cc_test_display_value($order['sede'])); ?></div>
</div>
<div class="col-md-8">
<label class="text-muted small d-block">Producto</label>
<div class="fw-bold"><?php echo nl2br(htmlspecialchars(cc_test_display_value($order['producto']))); ?></div>
</div>
<div class="col-md-2">
<label class="text-muted small d-block">Cantidad</label>
<div class="fw-bold"><?php echo htmlspecialchars(cc_test_display_value($order['cantidad'], '1')); ?></div>
</div>
<div class="col-md-2">
<label class="text-muted small d-block">Precio</label>
<div class="fw-bold text-success"><?php echo htmlspecialchars($precioLabel); ?></div>
</div>
<div class="col-md-12">
<label class="text-muted small d-block">Observaciones del Drive</label>
<div class="p-3 bg-light rounded small"><?php echo nl2br(htmlspecialchars(cc_test_display_value($order['observaciones'], 'Sin observaciones'))); ?></div>
</div>
<div class="col-md-12">
<label class="text-muted small d-block">Coordenadas</label>
<?php if (!empty($order['coordenadas'])): ?>
<a href="<?php echo htmlspecialchars($order['coordenadas']); ?>" target="_blank" rel="noopener noreferrer" class="btn btn-sm btn-outline-secondary">Abrir ubicación</a>
<?php else: ?>
<div class="fw-bold">No registradas</div>
<?php endif; ?>
</div>
</div>
<hr>
<div class="mb-3">
<h4 class="h6 fw-bold"><i class="bi bi-clock-history"></i> Historial de llamadas</h4>
<?php
$stmtHist = db()->prepare("SELECT h.*, u.nombre as asesor FROM historial_llamadas h LEFT JOIN usuarios u ON h.asesor_id = u.id WHERE h.pedido_id = ? ORDER BY h.fecha_llamada DESC");
$stmtHist->execute([$order['source_key']]);
$historial = $stmtHist->fetchAll(PDO::FETCH_ASSOC);
?>
<?php if (empty($historial)): ?>
<p class="text-muted small">No hay intentos de llamada registrados aún.</p>
<?php else: ?>
<div class="list-group list-group-flush border rounded">
<?php foreach ($historial as $h): ?>
<div class="list-group-item small py-2">
<div class="d-flex justify-content-between">
<span class="fw-bold"><?php echo htmlspecialchars($h['asesor'] ?? 'Desconocido'); ?></span>
<span class="text-muted"><?php echo date('d/m/Y H:i', strtotime($h['fecha_llamada'])); ?></span>
</div>
<div class="text-primary"><?php echo htmlspecialchars($h['resultado']); ?></div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
<hr>
<div class="col-md-4">
<label for="estado-<?php echo htmlspecialchars($order['source_key']); ?>" class="form-label">Estado de seguimiento</label>
<select class="form-select" id="estado-<?php echo htmlspecialchars($order['source_key']); ?>">
<?php foreach (['POR LLAMAR', 'REINTENTO', 'CONFIRMADO'] as $estadoOption): ?>
<option value="<?php echo htmlspecialchars($estadoOption); ?>" <?php echo $order['estado'] === $estadoOption ? 'selected' : ''; ?>>
<?php echo htmlspecialchars($estadoOption); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-8">
<label for="nota-<?php echo htmlspecialchars($order['source_key']); ?>" class="form-label">Nota interna de seguimiento</label>
<textarea class="form-control" id="nota-<?php echo htmlspecialchars($order['source_key']); ?>" rows="3" placeholder="Ejemplo: cliente pidió devolver la llamada en la tarde."><?php echo htmlspecialchars($order['nota_seguimiento']); ?></textarea>
</div>
</div>
</div>
<div class="modal-footer d-flex justify-content-between">
<div class="small text-muted">Los cambios se guardan solo en el módulo de prueba.</div>
<div class="d-flex gap-2">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cerrar</button>
<button type="button" class="btn btn-primary" onclick="guardarSeguimiento('<?php echo htmlspecialchars($order['source_key']); ?>')">Guardar seguimiento</button>
</div>
</div>
</div>
</div>
</div>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</section>
<?php endif; ?>
</main>
<script>
function guardarSeguimiento(sourceKey) {
const estado = document.getElementById('estado-' + sourceKey)?.value || 'POR LLAMAR';
const nota = document.getElementById('nota-' + sourceKey)?.value || '';
const body = new URLSearchParams({
source_key: sourceKey,
estado: estado,
nota_seguimiento: nota
});
fetch('update_callcenter_test_tracking.php', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
body: body.toString()
})
.then(response => response.json())
.then(data => {
if (!data.success) {
throw new Error(data.message || 'No se pudo guardar');
}
window.location.reload();
})
.catch(error => {
alert(error.message || 'Ocurrió un error al guardar el seguimiento.');
});
}
function registrarLlamada(sourceKey) {
const body = new URLSearchParams({
pedido_id: sourceKey,
resultado: 'Llamada iniciada',
observacion: 'Clic en botón llamar desde el panel'
});
fetch('save_llamada.php', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
body: body.toString()
})
.then(response => response.json())
.then(data => {
if (data.success) {
console.log('Llamada registrada correctamente');
// Opcional: recargar para actualizar el contador,
// pero como abre la app de llamadas, tal vez sea mejor no interrumpir.
// window.location.reload();
}
})
.catch(error => console.error('Error al registrar llamada:', error));
}
</script>
<?php require_once 'layout_footer.php'; ?>