diff --git a/admin/cliente-detalle.php b/admin/cliente-detalle.php new file mode 100644 index 0000000..4fa0f5b --- /dev/null +++ b/admin/cliente-detalle.php @@ -0,0 +1,649 @@ + 0; +$cliente = null; +$errors = []; +$success = ''; + +// Labels for tramites +$tipoLabels = [ + 'visa' => 'Visa', + 'sentri' => 'Sentri/Global', + 'pasaporte' => 'Pasaporte', + 'adelanto_cita' => 'Adelanto Cita', + 'doble_nacionalidad' => 'Doble Nacionalidad', +]; +$estadoLabels = [ + 'nuevo' => 'Nuevo', + 'en_proceso' => 'En Proceso', + 'en_revision' => 'En Revisión', + 'completado' => 'Completado', + 'cancelado' => 'Cancelado', +]; + +// ── Handle POST actions ───────────────────────────────────────── +if ($_SERVER['REQUEST_METHOD'] === 'POST' && csrfValidate()) { + $action = $_POST['action'] ?? 'save_client'; + + switch ($action) { + + // ── Save client (create / update) ───────────────────────── + case 'save_client': + $nombre = trim($_POST['nombre'] ?? ''); + $telefono = trim($_POST['telefono'] ?? ''); + $email = trim($_POST['email'] ?? ''); + $direccion = trim($_POST['direccion'] ?? ''); + $notas = trim($_POST['notas'] ?? ''); + + if ($nombre === '') { + $errors[] = 'El nombre es obligatorio.'; + } + if ($email !== '' && !filter_var($email, FILTER_VALIDATE_EMAIL)) { + $errors[] = 'El email no es válido.'; + } + + if (empty($errors)) { + if ($isEdit) { + $stmt = $db->prepare("UPDATE clientes SET nombre=?, telefono=?, email=?, direccion=?, notas=? WHERE id=?"); + $stmt->execute([$nombre, $telefono, $email, $direccion, $notas, $clienteId]); + header("Location: cliente-detalle.php?id=$clienteId&saved=1"); + exit; + } else { + $stmt = $db->prepare("INSERT INTO clientes (nombre, telefono, email, direccion, notas) VALUES (?,?,?,?,?)"); + $stmt->execute([$nombre, $telefono, $email, $direccion, $notas]); + $newId = $db->lastInsertId(); + + // If created from solicitud, link them + $fromSolicitud = (int)($_POST['from_solicitud'] ?? 0); + if ($fromSolicitud > 0) { + $stmt2 = $db->prepare("UPDATE solicitudes SET cliente_id=?, estado='convertida' WHERE id=?"); + $stmt2->execute([$newId, $fromSolicitud]); + } + + header("Location: cliente-detalle.php?id=$newId&saved=1"); + exit; + } + } + break; + + // ── Add credential ──────────────────────────────────────── + case 'add_credential': + if (!$isEdit) break; + + $portal = trim($_POST['cred_portal'] ?? ''); + $usuario = trim($_POST['cred_usuario'] ?? ''); + $password = $_POST['cred_password'] ?? ''; + $credNotas = trim($_POST['cred_notas'] ?? ''); + + if ($portal === '' || $usuario === '' || $password === '') { + $errors[] = 'Portal, usuario y contraseña son obligatorios.'; + } else { + $passwordEnc = encryptData($password); + $stmt = $db->prepare("INSERT INTO credenciales (cliente_id, portal, usuario, password_enc, notas) VALUES (?,?,?,?,?)"); + $stmt->execute([$clienteId, $portal, $usuario, $passwordEnc, $credNotas]); + header("Location: cliente-detalle.php?id=$clienteId&cred_saved=1#credenciales"); + exit; + } + break; + + // ── Delete credential ───────────────────────────────────── + case 'delete_credential': + if (!$isEdit) break; + + $credId = (int)($_POST['cred_id'] ?? 0); + if ($credId > 0) { + $stmt = $db->prepare("DELETE FROM credenciales WHERE id=? AND cliente_id=?"); + $stmt->execute([$credId, $clienteId]); + } + header("Location: cliente-detalle.php?id=$clienteId&cred_deleted=1#credenciales"); + exit; + + // ── Upload document ─────────────────────────────────────── + case 'upload_document': + if (!$isEdit) break; + + $docNombre = trim($_POST['doc_nombre'] ?? ''); + $file = $_FILES['doc_archivo'] ?? null; + + if ($docNombre === '') { + $errors[] = 'El nombre del documento es obligatorio.'; + } + if (!$file || $file['error'] !== UPLOAD_ERR_OK) { + $errors[] = 'Debe seleccionar un archivo válido.'; + } else { + // Validate extension + $ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION)); + if (!in_array($ext, ALLOWED_EXTENSIONS)) { + $errors[] = 'Tipo de archivo no permitido. Permitidos: ' . implode(', ', ALLOWED_EXTENSIONS); + } + // Validate size + if ($file['size'] > MAX_FILE_SIZE) { + $errors[] = 'El archivo excede el tamaño máximo de ' . (MAX_FILE_SIZE / 1024 / 1024) . ' MB.'; + } + } + + if (empty($errors)) { + // Create client upload directory + $uploadDir = UPLOAD_DIR . 'client_' . $clienteId . '/'; + if (!is_dir($uploadDir)) { + mkdir($uploadDir, 0755, true); + } + + // Generate unique filename + $safeFilename = preg_replace('/[^a-zA-Z0-9_\-.]/', '_', $file['name']); + $filename = time() . '_' . $safeFilename; + $destPath = $uploadDir . $filename; + + if (move_uploaded_file($file['tmp_name'], $destPath)) { + $rutaArchivo = 'client_' . $clienteId . '/' . $filename; + $stmt = $db->prepare("INSERT INTO documentos (cliente_id, nombre, ruta_archivo, tipo) VALUES (?,?,?,?)"); + $stmt->execute([$clienteId, $docNombre, $rutaArchivo, $ext]); + header("Location: cliente-detalle.php?id=$clienteId&doc_saved=1#documentos"); + exit; + } else { + $errors[] = 'Error al subir el archivo. Intente de nuevo.'; + } + } + break; + + // ── Delete document ─────────────────────────────────────── + case 'delete_document': + if (!$isEdit) break; + + $docId = (int)($_POST['doc_id'] ?? 0); + if ($docId > 0) { + // Get file path before deleting + $stmt = $db->prepare("SELECT ruta_archivo FROM documentos WHERE id=? AND cliente_id=?"); + $stmt->execute([$docId, $clienteId]); + $doc = $stmt->fetch(); + + if ($doc) { + // Remove file from disk + $filePath = UPLOAD_DIR . $doc['ruta_archivo']; + if (file_exists($filePath)) { + unlink($filePath); + } + // Remove DB record + $stmt = $db->prepare("DELETE FROM documentos WHERE id=? AND cliente_id=?"); + $stmt->execute([$docId, $clienteId]); + } + } + header("Location: cliente-detalle.php?id=$clienteId&doc_deleted=1#documentos"); + exit; + } +} + +// ── Handle download (GET) ─────────────────────────────────────── +if (isset($_GET['download']) && $isEdit) { + $docId = (int)$_GET['download']; + $stmt = $db->prepare("SELECT nombre, ruta_archivo, tipo FROM documentos WHERE id=? AND cliente_id=?"); + $stmt->execute([$docId, $clienteId]); + $doc = $stmt->fetch(); + + if ($doc) { + $filePath = UPLOAD_DIR . $doc['ruta_archivo']; + if (file_exists($filePath)) { + $mimeTypes = [ + 'pdf' => 'application/pdf', + 'jpg' => 'image/jpeg', + 'jpeg' => 'image/jpeg', + 'png' => 'image/png', + 'doc' => 'application/msword', + 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + ]; + $mime = $mimeTypes[$doc['tipo']] ?? 'application/octet-stream'; + $downloadName = $doc['nombre'] . '.' . $doc['tipo']; + + header('Content-Type: ' . $mime); + header('Content-Disposition: attachment; filename="' . $downloadName . '"'); + header('Content-Length: ' . filesize($filePath)); + header('Cache-Control: no-cache, must-revalidate'); + readfile($filePath); + exit; + } + } + // File not found — continue to page with error + $errors[] = 'Documento no encontrado.'; +} + +// ── Load client data for edit mode ────────────────────────────── +if ($isEdit) { + $stmt = $db->prepare("SELECT * FROM clientes WHERE id=?"); + $stmt->execute([$clienteId]); + $cliente = $stmt->fetch(); + + if (!$cliente) { + header('Location: clientes.php'); + exit; + } + + // Fetch credentials + $stmtCred = $db->prepare("SELECT * FROM credenciales WHERE cliente_id=? ORDER BY created_at DESC"); + $stmtCred->execute([$clienteId]); + $credenciales = $stmtCred->fetchAll(); + + // Fetch documents + $stmtDoc = $db->prepare("SELECT * FROM documentos WHERE cliente_id=? ORDER BY created_at DESC"); + $stmtDoc->execute([$clienteId]); + $documentos = $stmtDoc->fetchAll(); + + // Fetch tramites + $stmtTram = $db->prepare("SELECT * FROM tramites WHERE cliente_id=? ORDER BY fecha_solicitud DESC, created_at DESC"); + $stmtTram->execute([$clienteId]); + $tramites = $stmtTram->fetchAll(); +} + +// ── Pre-fill from solicitud if creating new ───────────────────── +$fromSolicitud = 0; +$prefill = ['nombre' => '', 'telefono' => '', 'email' => '', 'direccion' => '', 'notas' => '']; + +if (!$isEdit && isset($_GET['from_solicitud'])) { + $fromSolicitud = (int)$_GET['from_solicitud']; + if ($fromSolicitud > 0) { + $stmt = $db->prepare("SELECT nombre, telefono, email FROM solicitudes WHERE id=?"); + $stmt->execute([$fromSolicitud]); + $sol = $stmt->fetch(); + if ($sol) { + $prefill['nombre'] = $sol['nombre']; + $prefill['telefono'] = $sol['telefono'] ?? ''; + $prefill['email'] = $sol['email'] ?? ''; + } + } +} + +// If POST had errors, preserve submitted data +if (!empty($errors) && ($_POST['action'] ?? '') === 'save_client') { + $prefill['nombre'] = $_POST['nombre'] ?? ''; + $prefill['telefono'] = $_POST['telefono'] ?? ''; + $prefill['email'] = $_POST['email'] ?? ''; + $prefill['direccion'] = $_POST['direccion'] ?? ''; + $prefill['notas'] = $_POST['notas'] ?? ''; +} + +// Success messages from redirects +if (isset($_GET['saved'])) $success = 'Datos del cliente guardados correctamente.'; +if (isset($_GET['cred_saved'])) $success = 'Credencial agregada correctamente.'; +if (isset($_GET['cred_deleted'])) $success = 'Credencial eliminada.'; +if (isset($_GET['doc_saved'])) $success = 'Documento subido correctamente.'; +if (isset($_GET['doc_deleted'])) $success = 'Documento eliminado.'; + +$pageTitle = $isEdit ? 'Cliente: ' . ($cliente['nombre'] ?? '') : 'Nuevo Cliente'; +?> + +
| Portal | +Usuario | +Contraseña | +Notas | +Acciones | +
|---|---|---|---|---|
| = htmlspecialchars($cred['portal']) ?> | += htmlspecialchars($cred['usuario']) ?> | ++ + •••••••• + + + + | += htmlspecialchars($cred['notas'] ?? '-') ?> | ++ + | +
No hay credenciales registradas para este cliente.
+ + + +| Nombre | +Tipo | +Fecha | +Acciones | +
|---|---|---|---|
| + + = htmlspecialchars($doc['nombre']) ?> + | += htmlspecialchars(strtoupper($doc['tipo'] ?? 'N/A')) ?> | += date('d/m/Y', strtotime($doc['created_at'])) ?> | ++ + + + + | +
No hay documentos subidos para este cliente.
+ + + +| Tipo | +Estado | +Fecha Solicitud | +Fecha Cita | +Precio | +Acción | +
|---|---|---|---|---|---|
| = htmlspecialchars($tipoLabels[$t['tipo']] ?? $t['tipo']) ?> | ++ + = $estadoLabels[$t['estado']] ?? htmlspecialchars($t['estado']) ?> + + | += $t['fecha_solicitud'] ? date('d/m/Y', strtotime($t['fecha_solicitud'])) : '-' ?> | += $t['fecha_cita'] ? date('d/m/Y', strtotime($t['fecha_cita'])) : '-' ?> | += $t['precio'] ? '$' . number_format($t['precio'], 2) : '-' ?> | ++ + Ver + + | +
No hay trámites registrados para este cliente.
+ Crear primer trámite +Gestión de clientes registrados
+= $q !== '' ? 'No se encontraron clientes con esa búsqueda.' : 'No hay clientes registrados aún.' ?>
+ + Ver todos + +| Nombre | +Teléfono | +Trámites | +Fecha Registro | +Acción | +|
|---|---|---|---|---|---|
| = htmlspecialchars($c['nombre']) ?> | += htmlspecialchars($c['telefono'] ?: '-') ?> | += htmlspecialchars($c['email'] ?: '-') ?> | ++ = (int)$c['total_tramites'] ?> + | += date('d/m/Y', strtotime($c['created_at'])) ?> | ++ + Ver + + | +