383 lines
17 KiB
PHP

<?php
/**
* detalle.php — Vista detalle de un oficio con historial, adjuntos y comentarios
*/
require_once __DIR__ . '/../../config/config.php';
require_once __DIR__ . '/../../controllers/AuthController.php';
require_once __DIR__ . '/../../models/Oficio.php';
require_once __DIR__ . '/../../models/Usuario.php';
AuthController::requerirAuth();
$id = (int)($_GET['id'] ?? 0);
if (!$id) redirect(APP_URL . '/views/oficios/lista.php');
$model = new OficioModel();
$oficio = $model->buscarPorId($id);
if (!$oficio) {
http_response_code(404);
redirect(APP_URL . '/views/oficios/lista.php?error='.urlencode('Oficio no encontrado.'));
}
// Marcar notificación si viene de ella
if (isset($_GET['notif'])) {
$db = getDB();
$db->prepare("UPDATE notificaciones SET leida=1,leida_at=NOW() WHERE id=? AND usuario_id=?")
->execute([(int)$_GET['notif'], $_SESSION['usuario_id']]);
}
$historial = $model->historial($id);
$comentarios = $model->comentarios($id, !AuthController::esAdmin());
$etiquetas = $model->etiquetasDeOficio($id);
$adjuntos = getDB()->prepare("SELECT * FROM documentos_adjuntos WHERE oficio_id=? ORDER BY created_at DESC");
$adjuntos->execute([$id]);
$adjuntos = $adjuntos->fetchAll();
$usuarios = (new UsuarioModel())->usuariosParaSelector();
$esAdmin = AuthController::esAdmin();
$userId = $_SESSION['usuario_id'];
// Calcular semáforo
$semaforo = $oficio['semaforo'] ?? 'vigente';
$semaforoColors = ['vigente'=>'success','proximo'=>'warning','vencido'=>'danger','completado'=>'info'];
$semaColor = $semaforoColors[$semaforo] ?? 'secondary';
$pageTitle = 'Oficio: '.$oficio['numero_oficio'];
$activeNav = $oficio['tipo'] === 'recibido' ? 'entrada' : 'salida';
include __DIR__ . '/../../views/layout/header.php';
include __DIR__ . '/../../views/layout/sidebar.php';
include __DIR__ . '/../../views/layout/topbar.php';
?>
<div class="page-content">
<div class="breadcrumb">
<a href="<?= APP_URL ?>/dashboard.php"><i class="fa-solid fa-house"></i></a>
<i class="fa-solid fa-chevron-right sep"></i>
<a href="<?= APP_URL ?>/views/oficios/lista.php?tipo=<?= $oficio['tipo'] ?>">
<?= $oficio['tipo']==='recibido' ? 'Entrada' : 'Salida' ?>
</a>
<i class="fa-solid fa-chevron-right sep"></i>
<span><?= htmlspecialchars($oficio['numero_oficio']) ?></span>
</div>
<!-- Flash messages -->
<?php
$success = $_GET['success'] ?? null;
$error = $_GET['error'] ?? null;
?>
<?php if ($success): ?>
<div class="alert alert-success"><i class="fa-solid fa-circle-check"></i> <?= htmlspecialchars($success) ?></div>
<?php endif; ?>
<?php if ($error): ?>
<div class="alert alert-danger"><i class="fa-solid fa-circle-exclamation"></i> <?= htmlspecialchars($error) ?></div>
<?php endif; ?>
<!-- Encabezado del oficio -->
<div class="page-header">
<div class="page-header-content">
<div class="d-flex align-items-center gap-3 flex-wrap">
<h1><?= htmlspecialchars($oficio['numero_oficio']) ?></h1>
<span class="badge badge-<?= $semaColor ?> semaforo-<?= $semaforo ?>" style="font-size:.85rem;padding:.3rem .8rem">
<?= ucfirst($semaforo) ?>
</span>
<?php if ($oficio['es_confidencial']): ?>
<span class="badge badge-warning"><i class="fa-solid fa-lock"></i> Confidencial</span>
<?php endif; ?>
</div>
<p style="margin-top:.4rem;font-size:.9rem;color:var(--text-muted)">
<?= htmlspecialchars($oficio['asunto']) ?>
</p>
</div>
<div class="d-flex gap-2 flex-wrap">
<a href="<?= APP_URL ?>/views/oficios/editar.php?id=<?= $id ?>" class="btn btn-warning">
<i class="fa-solid fa-pen"></i> Editar
</a>
<a href="<?= APP_URL ?>/controllers/OficioController.php?action=pdf&id=<?= $id ?>" class="btn btn-primary" target="_blank">
<i class="fa-solid fa-file-pdf"></i> PDF
</a>
<button class="btn btn-secondary" data-modal="modalDerivar">
<i class="fa-solid fa-reply-all"></i> Derivar
</button>
<a href="<?= APP_URL ?>/controllers/OficioController.php?action=eliminar&id=<?= $id ?>"
class="btn btn-danger"
data-confirm="¿Mover este oficio a la papelera?">
<i class="fa-solid fa-trash"></i>
</a>
</div>
</div>
<div class="grid-2" style="grid-template-columns:1.4fr 1fr;gap:1.5rem;align-items:start">
<!-- Columna izquierda: Datos + Adjuntos -->
<div>
<!-- Datos principales -->
<div class="card mb-4">
<div class="card-header">
<i class="fa-solid fa-file-lines text-primary"></i>
<span class="card-title">Datos del Oficio</span>
</div>
<div class="card-body">
<div class="grid-2" style="gap:1rem;margin-bottom:1rem">
<div>
<div class="fs-sm text-muted fw-600">TIPO</div>
<div style="margin-top:.2rem">
<?= $oficio['tipo']==='recibido'
? '<span class="badge badge-info"><i class="fa-solid fa-inbox"></i> Recibido</span>'
: '<span class="badge badge-secondary"><i class="fa-solid fa-paper-plane"></i> Enviado</span>' ?>
</div>
</div>
<div>
<div class="fs-sm text-muted fw-600">PRIORIDAD</div>
<div style="margin-top:.2rem">
<span class="badge badge-<?= ['alta'=>'danger','media'=>'warning','baja'=>'success'][$oficio['prioridad']]??'secondary' ?>">
<?= ucfirst($oficio['prioridad']) ?>
</span>
</div>
</div>
<div>
<div class="fs-sm text-muted fw-600">ESTADO</div>
<div style="margin-top:.2rem">
<span class="badge badge-<?= ['recibido'=>'primary','en_proceso'=>'warning','respondido'=>'success','vencido'=>'danger','archivado'=>'secondary'][$oficio['estado']]??'secondary' ?>">
<?= ucfirst(str_replace('_',' ',$oficio['estado'])) ?>
</span>
</div>
</div>
<div>
<div class="fs-sm text-muted fw-600">RESPONSABLE</div>
<div style="margin-top:.2rem;font-weight:600"><?= htmlspecialchars($oficio['responsable_nombre'] ?? '—') ?></div>
</div>
<div>
<div class="fs-sm text-muted fw-600">FECHA RECEPCIÓN</div>
<div style="margin-top:.2rem"><?= date('d/m/Y', strtotime($oficio['fecha_recepcion'])) ?></div>
</div>
<div>
<div class="fs-sm text-muted fw-600">FECHA VENCIMIENTO</div>
<div style="margin-top:.2rem">
<?php if ($oficio['fecha_vencimiento']): ?>
<strong class="<?= $semaforo==='vencido'?'text-danger':($semaforo==='proximo'?'text-warning':'') ?>">
<?= date('d/m/Y', strtotime($oficio['fecha_vencimiento'])) ?>
</strong>
<?php if ($oficio['dias_para_vencer'] !== null): ?>
<div class="fs-sm text-muted">
<?= $oficio['dias_para_vencer'] < 0
? abs($oficio['dias_para_vencer']).' día(s) vencido'
: ($oficio['dias_para_vencer'] === '0' ? 'Vence HOY' : $oficio['dias_para_vencer'].' día(s) restante(s)') ?>
</div>
<?php endif; ?>
<?php else: ?>—<?php endif; ?>
</div>
</div>
</div>
<div style="margin-bottom:1rem">
<div class="fs-sm text-muted fw-600">REMITENTE</div>
<div style="margin-top:.2rem"><?= htmlspecialchars($oficio['remitente']) ?></div>
</div>
<div style="margin-bottom:1rem">
<div class="fs-sm text-muted fw-600">DESTINATARIO</div>
<div style="margin-top:.2rem"><?= htmlspecialchars($oficio['destinatario']) ?></div>
</div>
<div style="margin-bottom:1rem">
<div class="fs-sm text-muted fw-600">ASUNTO</div>
<div style="margin-top:.2rem"><?= htmlspecialchars($oficio['asunto']) ?></div>
</div>
<?php if ($oficio['descripcion']): ?>
<div style="margin-bottom:.5rem">
<div class="fs-sm text-muted fw-600">DESCRIPCIÓN</div>
<div style="margin-top:.2rem;font-size:.85rem;line-height:1.7"><?= nl2br(htmlspecialchars($oficio['descripcion'])) ?></div>
</div>
<?php endif; ?>
<?php if ($etiquetas): ?>
<div style="margin-top:1rem">
<div class="fs-sm text-muted fw-600 mb-2">ETIQUETAS</div>
<div class="d-flex gap-2 flex-wrap">
<?php foreach ($etiquetas as $et): ?>
<span class="badge" style="background:<?= $et['color'] ?>25;color:<?= $et['color'] ?>">
<i class="fa-solid <?= $et['icono'] ?>"></i>
<?= htmlspecialchars($et['nombre']) ?>
</span>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
</div>
</div>
<!-- Documentos adjuntos -->
<?php if (!empty($adjuntos)): ?>
<div class="card mb-4">
<div class="card-header">
<i class="fa-solid fa-paperclip text-primary"></i>
<span class="card-title">Documentos Adjuntos (<?= count($adjuntos) ?>)</span>
</div>
<div class="card-body" style="display:flex;flex-wrap:wrap;gap:.75rem">
<?php foreach ($adjuntos as $adj):
$ext = strtolower(pathinfo($adj['nombre_original'], PATHINFO_EXTENSION));
$iconos = ['pdf'=>'fa-file-pdf','doc'=>'fa-file-word','docx'=>'fa-file-word','xls'=>'fa-file-excel','xlsx'=>'fa-file-excel','jpg'=>'fa-file-image','jpeg'=>'fa-file-image','png'=>'fa-file-image'];
$iconColor = ['pdf'=>'#ef4444','doc'=>'#3b82f6','docx'=>'#3b82f6','xls'=>'#10b981','xlsx'=>'#10b981','jpg'=>'#f59e0b','jpeg'=>'#f59e0b','png'=>'#f59e0b'];
?>
<a href="<?= APP_URL ?>/uploads/<?= $adj['ruta'] ?>"
target="_blank"
style="display:flex;align-items:center;gap:.5rem;padding:.5rem .875rem;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius-sm);text-decoration:none;color:var(--text);font-size:.8rem;transition:all .2s"
onmouseover="this.style.borderColor='var(--primary)'"
onmouseout="this.style.borderColor='var(--border)'">
<i class="fa-solid <?= $iconos[$ext]??'fa-file' ?>" style="color:<?= $iconColor[$ext]??'var(--primary)' ?>;font-size:1.1rem"></i>
<div>
<div style="font-weight:600;max-width:180px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis">
<?= htmlspecialchars($adj['nombre_original']) ?>
</div>
<div style="color:var(--text-muted);font-size:.7rem">
<?= number_format($adj['tamanio']/1024, 1) ?> KB
</div>
</div>
<i class="fa-solid fa-download" style="color:var(--text-muted);margin-left:.25rem"></i>
</a>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
</div>
<!-- Columna derecha: Comentarios + Historial -->
<div>
<!-- Tabs -->
<div data-tabs>
<div class="tabs">
<button class="tab-btn active" data-tab="tabComentarios">
<i class="fa-solid fa-comments"></i> Comentarios (<?= count($comentarios) ?>)
</button>
<button class="tab-btn" data-tab="tabHistorial">
<i class="fa-solid fa-clock-rotate-left"></i> Historial (<?= count($historial) ?>)
</button>
</div>
<!-- Tab comentarios -->
<div id="tabComentarios" class="tab-content active">
<!-- Nuevo comentario -->
<form method="POST" action="<?= APP_URL ?>/controllers/OficioController.php?action=comentar" class="card mb-3">
<?= csrfField() ?>
<input type="hidden" name="oficio_id" value="<?= $id ?>">
<div class="card-body">
<textarea name="comentario" class="form-control" rows="3" placeholder="Escribe un comentario…" required></textarea>
<?php if ($esAdmin): ?>
<label style="display:flex;align-items:center;gap:.4rem;margin-top:.5rem;font-size:.8rem;cursor:pointer">
<input type="checkbox" name="es_privado" value="1">
<i class="fa-solid fa-eye-slash text-warning"></i> Comentario privado (solo admins)
</label>
<?php endif; ?>
</div>
<div class="card-footer">
<button type="submit" class="btn btn-primary btn-sm">
<i class="fa-solid fa-paper-plane"></i> Comentar
</button>
</div>
</form>
<!-- Lista de comentarios -->
<div class="comment-thread">
<?php if (empty($comentarios)): ?>
<div style="text-align:center;padding:1.5rem;color:var(--text-muted);font-size:.83rem">
<i class="fa-solid fa-comment-slash" style="font-size:1.5rem;display:block;margin-bottom:.5rem;opacity:.3"></i>
Sin comentarios aún
</div>
<?php else: ?>
<?php foreach ($comentarios as $c):
$iniciales = implode('', array_map(fn($p)=>strtoupper($p[0]), array_slice(explode(' ', $c['autor_nombre']), 0, 2)));
?>
<div class="comment-item">
<div class="comment-avatar"><?= $iniciales ?></div>
<div class="comment-bubble" style="<?= $c['es_privado'] ? 'border-color:var(--warning);background:rgba(245,158,11,.05)' : '' ?>">
<div class="comment-meta">
<strong><?= htmlspecialchars($c['autor_nombre']) ?></strong>
&middot; <?= date('d/m/Y H:i', strtotime($c['created_at'])) ?>
<?php if ($c['es_privado']): ?>
<span class="badge badge-warning" style="font-size:.65rem;margin-left:.3rem"><i class="fa-solid fa-eye-slash"></i> Privado</span>
<?php endif; ?>
</div>
<div class="comment-text"><?= nl2br(htmlspecialchars($c['comentario'])) ?></div>
</div>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
</div>
<!-- Tab historial -->
<div id="tabHistorial" class="tab-content">
<div class="timeline">
<?php if (empty($historial)): ?>
<p class="text-muted fs-sm">Sin historial registrado.</p>
<?php else: ?>
<?php foreach ($historial as $h): ?>
<div class="timeline-item">
<div class="timeline-date">
<?= date('d/m/Y H:i', strtotime($h['created_at'])) ?>
</div>
<div class="timeline-body">
<span class="timeline-author"><?= htmlspecialchars($h['usuario_nombre'] ?? 'Sistema') ?></span>
· <span class="badge badge-<?= ['crear'=>'success','editar'=>'info','eliminar'=>'danger','restaurar'=>'warning','derivar'=>'primary','escalacion'=>'secondary'][$h['accion']]??'secondary' ?>">
<?= ucfirst($h['accion']) ?>
</span>
<?php if ($h['detalle']): ?>
<div style="margin-top:.2rem;font-size:.78rem;color:var(--text-muted)">
<?= htmlspecialchars($h['detalle']) ?>
</div>
<?php endif; ?>
</div>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
</div>
</div>
</div>
</div>
</div><!-- /.page-content -->
<!-- Modal: Derivar oficio -->
<div class="modal-overlay" id="modalDerivar">
<div class="modal-box">
<div class="modal-header">
<i class="fa-solid fa-reply-all text-primary"></i>
<span class="modal-title">Derivar Oficio</span>
<button class="btn-icon" data-modal-close><i class="fa-solid fa-xmark"></i></button>
</div>
<form method="POST" action="<?= APP_URL ?>/controllers/OficioController.php?action=derivar">
<?= csrfField() ?>
<input type="hidden" name="id" value="<?= $id ?>">
<div class="modal-body">
<div class="form-group">
<label class="form-label">Nuevo Responsable <span class="required">*</span></label>
<select class="form-control" name="nuevo_responsable" required>
<option value="">— Seleccionar usuario —</option>
<?php foreach ($usuarios as $u): ?>
<option value="<?= $u['id'] ?>"><?= htmlspecialchars($u['nombre_completo']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group">
<label class="form-label">Comentario de Derivación <span class="required">*</span></label>
<textarea class="form-control" name="comentario_derivacion" rows="3"
placeholder="Explica el motivo de la derivación…" required></textarea>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-modal-close>Cancelar</button>
<button type="submit" class="btn btn-primary">
<i class="fa-solid fa-reply-all"></i> Derivar
</button>
</div>
</form>
</div>
</div>
<?php include __DIR__ . '/../../views/layout/footer.php'; ?>