Files
gestoria-lp/admin/cliente-detalle.php
2026-03-02 00:25:50 +00:00

650 lines
29 KiB
PHP

<?php
$pageTitle = 'Detalle Cliente';
require_once __DIR__ . '/../includes/db.php';
require_once __DIR__ . '/../includes/encryption.php';
require_once __DIR__ . '/../includes/config.php';
require_once __DIR__ . '/includes/admin-header.php';
$db = getDB();
// Determine mode: edit (id exists) or create (no id)
$clienteId = isset($_GET['id']) ? (int)$_GET['id'] : 0;
$isEdit = $clienteId > 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&oacute;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&aacute;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&ntilde;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&aacute;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&ntilde;o m&aacute;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';
?>
<div class="admin-content__header">
<h1>
<i class="fas fa-<?= $isEdit ? 'user-edit' : 'user-plus' ?>"></i>
<?= $isEdit ? htmlspecialchars($cliente['nombre']) : 'Nuevo Cliente' ?>
</h1>
<p>
<a href="clientes.php" class="btn btn--sm btn--secondary">
<i class="fas fa-arrow-left"></i> Volver a Clientes
</a>
</p>
</div>
<!-- Alerts -->
<?php if ($success): ?>
<div class="alert alert--success alert--dismissible">
<i class="fas fa-check-circle"></i>
<span><?= $success ?></span>
<button class="alert__close" onclick="this.parentElement.remove()">&times;</button>
</div>
<?php endif; ?>
<?php if (!empty($errors)): ?>
<div class="alert alert--danger">
<i class="fas fa-exclamation-circle"></i>
<div>
<?php foreach ($errors as $err): ?>
<div><?= $err ?></div>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
<!-- ============================================================
SECTION A: Client Data Form
============================================================ -->
<div class="card" style="margin-bottom: 1.5rem;">
<div class="card__header">
<h2><i class="fas fa-id-card"></i> Datos del Cliente</h2>
</div>
<div class="card__body">
<form method="POST" action="cliente-detalle.php<?= $isEdit ? '?id=' . $clienteId : '' ?>">
<?= csrfField() ?>
<input type="hidden" name="action" value="save_client">
<?php if ($fromSolicitud > 0): ?>
<input type="hidden" name="from_solicitud" value="<?= $fromSolicitud ?>">
<?php endif; ?>
<div class="form-row">
<div class="form-group">
<label>Nombre <span class="required">*</span></label>
<input type="text" name="nombre" class="form-control" required
value="<?= htmlspecialchars($isEdit ? $cliente['nombre'] : $prefill['nombre']) ?>">
</div>
<div class="form-group">
<label>Tel&eacute;fono</label>
<input type="text" name="telefono" class="form-control"
value="<?= htmlspecialchars($isEdit ? ($cliente['telefono'] ?? '') : $prefill['telefono']) ?>">
</div>
</div>
<div class="form-row">
<div class="form-group">
<label>Email</label>
<input type="email" name="email" class="form-control"
value="<?= htmlspecialchars($isEdit ? ($cliente['email'] ?? '') : $prefill['email']) ?>">
</div>
</div>
<div class="form-group">
<label>Direcci&oacute;n</label>
<textarea name="direccion" class="form-control" rows="2"><?= htmlspecialchars($isEdit ? ($cliente['direccion'] ?? '') : $prefill['direccion']) ?></textarea>
</div>
<div class="form-group">
<label>Notas</label>
<textarea name="notas" class="form-control" rows="3"><?= htmlspecialchars($isEdit ? ($cliente['notas'] ?? '') : $prefill['notas']) ?></textarea>
</div>
<div style="display: flex; gap: 0.75rem; align-items: center;">
<button type="submit" class="btn btn--primary">
<i class="fas fa-save"></i> <?= $isEdit ? 'Guardar Cambios' : 'Crear Cliente' ?>
</button>
<?php if ($isEdit): ?>
<span style="font-size: var(--admin-font-xs); color: var(--admin-gray-500);">
Registrado: <?= date('d/m/Y H:i', strtotime($cliente['created_at'])) ?>
<?php if ($cliente['updated_at'] && $cliente['updated_at'] !== $cliente['created_at']): ?>
&mdash; Actualizado: <?= date('d/m/Y H:i', strtotime($cliente['updated_at'])) ?>
<?php endif; ?>
</span>
<?php endif; ?>
</div>
</form>
</div>
</div>
<?php if ($isEdit): ?>
<!-- ============================================================
SECTION B: Credentials
============================================================ -->
<div class="card" id="credenciales" style="margin-bottom: 1.5rem;">
<div class="card__header">
<h2><i class="fas fa-key"></i> Credenciales de Portales</h2>
</div>
<div class="card__body">
<!-- Existing credentials -->
<?php if (!empty($credenciales)): ?>
<div class="table-responsive" style="margin-bottom: 1.5rem;">
<table class="admin-table">
<thead>
<tr>
<th>Portal</th>
<th>Usuario</th>
<th>Contrase&ntilde;a</th>
<th>Notas</th>
<th>Acciones</th>
</tr>
</thead>
<tbody>
<?php foreach ($credenciales as $cred): ?>
<tr>
<td><strong><?= htmlspecialchars($cred['portal']) ?></strong></td>
<td><?= htmlspecialchars($cred['usuario']) ?></td>
<td>
<span class="cred-password-display">
<span class="cred-dots">&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;</span>
<span class="cred-plain" style="display:none;"><?= htmlspecialchars(decryptData($cred['password_enc'])) ?></span>
<button type="button" class="btn btn--sm btn--ghost cred-toggle-btn" onclick="toggleCredPassword(this)" title="Mostrar/ocultar contrase&ntilde;a">
<i class="fas fa-eye"></i>
</button>
</span>
</td>
<td><?= htmlspecialchars($cred['notas'] ?? '-') ?></td>
<td>
<form method="POST" style="display:inline;" onsubmit="return confirm('&iquest;Eliminar esta credencial? Esta acci&oacute;n no se puede deshacer.')">
<?= csrfField() ?>
<input type="hidden" name="action" value="delete_credential">
<input type="hidden" name="cred_id" value="<?= (int)$cred['id'] ?>">
<button type="submit" class="btn btn--sm btn--outline-danger" title="Eliminar">
<i class="fas fa-trash"></i>
</button>
</form>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php else: ?>
<p class="text-muted" style="margin-bottom: 1rem;">No hay credenciales registradas para este cliente.</p>
<?php endif; ?>
<!-- Add credential form -->
<div class="detail-section">
<h3 class="detail-section__title"><i class="fas fa-plus-circle"></i> Agregar Credencial</h3>
<form method="POST" action="cliente-detalle.php?id=<?= $clienteId ?>">
<?= csrfField() ?>
<input type="hidden" name="action" value="add_credential">
<div class="form-row">
<div class="form-group">
<label>Portal <span class="required">*</span></label>
<input type="text" name="cred_portal" class="form-control" placeholder="Ej: CEAC, Portal SRE, SAT..." required>
</div>
<div class="form-group">
<label>Usuario <span class="required">*</span></label>
<input type="text" name="cred_usuario" class="form-control" required>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label>Contrase&ntilde;a <span class="required">*</span></label>
<input type="text" name="cred_password" class="form-control" required autocomplete="off">
</div>
<div class="form-group">
<label>Notas</label>
<input type="text" name="cred_notas" class="form-control" placeholder="Notas adicionales...">
</div>
</div>
<button type="submit" class="btn btn--success btn--sm">
<i class="fas fa-plus"></i> Agregar Credencial
</button>
</form>
</div>
</div>
</div>
<!-- ============================================================
SECTION C: Documents
============================================================ -->
<div class="card" id="documentos" style="margin-bottom: 1.5rem;">
<div class="card__header">
<h2><i class="fas fa-folder-open"></i> Documentos</h2>
</div>
<div class="card__body">
<!-- Existing documents -->
<?php if (!empty($documentos)): ?>
<div class="table-responsive" style="margin-bottom: 1.5rem;">
<table class="admin-table">
<thead>
<tr>
<th>Nombre</th>
<th>Tipo</th>
<th>Fecha</th>
<th>Acciones</th>
</tr>
</thead>
<tbody>
<?php foreach ($documentos as $doc): ?>
<tr>
<td>
<i class="fas fa-file-<?= in_array($doc['tipo'], ['jpg','jpeg','png']) ? 'image' : ($doc['tipo'] === 'pdf' ? 'pdf' : 'word') ?>"></i>
<?= htmlspecialchars($doc['nombre']) ?>
</td>
<td><span class="badge badge--secondary"><?= htmlspecialchars(strtoupper($doc['tipo'] ?? 'N/A')) ?></span></td>
<td><?= date('d/m/Y', strtotime($doc['created_at'])) ?></td>
<td style="display: flex; gap: 0.5rem;">
<a href="cliente-detalle.php?id=<?= $clienteId ?>&download=<?= (int)$doc['id'] ?>" class="btn btn--sm btn--info" title="Descargar">
<i class="fas fa-download"></i>
</a>
<form method="POST" style="display:inline;" onsubmit="return confirm('&iquest;Eliminar este documento? El archivo se borrar&aacute; permanentemente.')">
<?= csrfField() ?>
<input type="hidden" name="action" value="delete_document">
<input type="hidden" name="doc_id" value="<?= (int)$doc['id'] ?>">
<button type="submit" class="btn btn--sm btn--outline-danger" title="Eliminar">
<i class="fas fa-trash"></i>
</button>
</form>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php else: ?>
<p class="text-muted" style="margin-bottom: 1rem;">No hay documentos subidos para este cliente.</p>
<?php endif; ?>
<!-- Upload document form -->
<div class="detail-section">
<h3 class="detail-section__title"><i class="fas fa-upload"></i> Subir Documento</h3>
<form method="POST" action="cliente-detalle.php?id=<?= $clienteId ?>" enctype="multipart/form-data">
<?= csrfField() ?>
<input type="hidden" name="action" value="upload_document">
<div class="form-row">
<div class="form-group">
<label>Nombre del Documento <span class="required">*</span></label>
<input type="text" name="doc_nombre" class="form-control" placeholder="Ej: Pasaporte, INE, Comprobante..." required>
</div>
<div class="form-group">
<label>Archivo <span class="required">*</span></label>
<input type="file" name="doc_archivo" class="form-control" accept=".pdf,.jpg,.jpeg,.png,.doc,.docx" required>
<span class="form-text">M&aacute;x. <?= MAX_FILE_SIZE / 1024 / 1024 ?> MB. Formatos: PDF, JPG, PNG, DOC, DOCX</span>
</div>
</div>
<button type="submit" class="btn btn--success btn--sm">
<i class="fas fa-upload"></i> Subir Documento
</button>
</form>
</div>
</div>
</div>
<!-- ============================================================
SECTION D: Process History (Tramites)
============================================================ -->
<div class="card" id="tramites" style="margin-bottom: 1.5rem;">
<div class="card__header">
<h2><i class="fas fa-file-alt"></i> Historial de Tr&aacute;mites</h2>
<a href="tramite-detalle.php?cliente_id=<?= $clienteId ?>" class="btn btn--sm btn--primary">
<i class="fas fa-plus"></i> Nuevo Tr&aacute;mite
</a>
</div>
<div class="card__body">
<?php if (!empty($tramites)): ?>
<div class="table-responsive">
<table class="admin-table">
<thead>
<tr>
<th>Tipo</th>
<th>Estado</th>
<th>Fecha Solicitud</th>
<th>Fecha Cita</th>
<th>Precio</th>
<th>Acci&oacute;n</th>
</tr>
</thead>
<tbody>
<?php foreach ($tramites as $t): ?>
<tr>
<td><?= htmlspecialchars($tipoLabels[$t['tipo']] ?? $t['tipo']) ?></td>
<td>
<span class="badge badge--<?= htmlspecialchars($t['estado']) ?>">
<?= $estadoLabels[$t['estado']] ?? htmlspecialchars($t['estado']) ?>
</span>
</td>
<td><?= $t['fecha_solicitud'] ? date('d/m/Y', strtotime($t['fecha_solicitud'])) : '-' ?></td>
<td><?= $t['fecha_cita'] ? date('d/m/Y', strtotime($t['fecha_cita'])) : '-' ?></td>
<td><?= $t['precio'] ? '$' . number_format($t['precio'], 2) : '-' ?></td>
<td>
<a href="tramite-detalle.php?id=<?= (int)$t['id'] ?>" class="btn btn--sm btn--primary">
<i class="fas fa-eye"></i> Ver
</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php else: ?>
<div class="table-empty">
<i class="fas fa-file-alt"></i>
<p>No hay tr&aacute;mites registrados para este cliente.</p>
<a href="tramite-detalle.php?cliente_id=<?= $clienteId ?>" class="btn btn--sm btn--outline">Crear primer tr&aacute;mite</a>
</div>
<?php endif; ?>
</div>
</div>
<?php endif; /* end isEdit sections */ ?>
<!-- JavaScript for credential password toggle -->
<script>
function toggleCredPassword(btn) {
var container = btn.closest('.cred-password-display');
var dots = container.querySelector('.cred-dots');
var plain = container.querySelector('.cred-plain');
var icon = btn.querySelector('i');
if (plain.style.display === 'none') {
dots.style.display = 'none';
plain.style.display = 'inline';
icon.classList.remove('fa-eye');
icon.classList.add('fa-eye-slash');
btn.title = 'Ocultar contrase\u00f1a';
} else {
dots.style.display = 'inline';
plain.style.display = 'none';
icon.classList.remove('fa-eye-slash');
icon.classList.add('fa-eye');
btn.title = 'Mostrar contrase\u00f1a';
}
}
</script>
<style>
.cred-password-display {
display: inline-flex;
align-items: center;
gap: 0.5rem;
}
.cred-dots {
letter-spacing: 2px;
color: var(--admin-gray-500);
font-size: var(--admin-font-sm);
}
.cred-plain {
font-family: 'Courier New', Courier, monospace;
font-size: var(--admin-font-sm);
background: var(--admin-gray-100);
padding: 0.125rem 0.5rem;
border-radius: var(--admin-radius-sm);
user-select: all;
}
</style>
<?php require_once __DIR__ . '/includes/admin-footer.php'; ?>