feat(home): eventos de hoy debajo de los módulos

- Lista con eventos del día agrupados: manuales, escrituras, trámites
- Visibilidad: eventos manuales y escrituras → todos los usuarios;
  trámites legales → solo creador, abogado nombrado o involucrado
  (mismo canView() que en el módulo de trámites)
- Cada evento es clickable y navega al módulo correspondiente
  (/calendario, /escrituras/?edit=ID, /tramites/?edit=CODIGO)
- Cards muestran: título + cliente + abogado + estatus, con tags
  de tipo y departamento; eventos manuales muestran rango horario
  y creador
This commit is contained in:
Soporte ZH Consultores
2026-06-04 21:05:28 -05:00
parent d26ab5e855
commit d75a2a5fda
+205
View File
@@ -1,12 +1,114 @@
<?php
declare(strict_types=1);
require_once __DIR__ . '/../src/Auth.php';
require_once __DIR__ . '/../src/Db.php';
require_once __DIR__ . '/../src/Apps.php';
$authed = Auth::isAuthenticated();
$user = $authed ? Auth::user() : ['email' => '', 'name' => ''];
$apps = $authed ? Apps::catalog() : [];
/**
* Devuelve los eventos del día agrupados:
* - manuales: visibles a todos
* - escrituras (fecha_solicitud=hoy): visibles a todos
* - trámites (solicitud/vencimiento/término=hoy): solo creador, abogado nombrado o involucrado
*/
function todays_events(array $u, int $myId, bool $isSocio): array {
try {
$pdo = Db::pdo();
$today = date('Y-m-d');
$manual = $pdo->prepare(
'SELECT id, titulo, tipo, fecha, hora_inicio, hora_fin, ubicacion, creado_por_nombre, creado_por
FROM calendar_events WHERE fecha = :d
ORDER BY (hora_inicio IS NULL), hora_inicio, id'
);
$manual->execute([':d' => $today]);
$manualRows = $manual->fetchAll();
$esc = $pdo->prepare(
'SELECT id, tramite, cliente, abogado, estatus
FROM escrituras_tramites WHERE DATE(fecha_solicitud) = :d
ORDER BY id DESC'
);
$esc->execute([':d' => $today]);
$escRows = $esc->fetchAll();
if ($isSocio) {
$tra = $pdo->prepare(
"SELECT id, codigo, departamento, nombre, tipo_tramite, tipo_proceso,
cliente, abogado, estatus, fecha_solicitud, fecha_vencimiento, fecha_termino
FROM tramites_legales
WHERE DATE(fecha_solicitud) = :d
OR fecha_vencimiento = :d2
OR fecha_termino = :d3
ORDER BY id DESC"
);
$tra->execute([':d' => $today, ':d2' => $today, ':d3' => $today]);
} else {
$tra = $pdo->prepare(
"SELECT DISTINCT t.id, t.codigo, t.departamento, t.nombre, t.tipo_tramite, t.tipo_proceso,
t.cliente, t.abogado, t.estatus, t.fecha_solicitud, t.fecha_vencimiento, t.fecha_termino
FROM tramites_legales t
LEFT JOIN users ua ON ua.name = t.abogado AND ua.activo = 1
LEFT JOIN tramites_involucrados ti ON ti.tramite_id = t.id AND ti.user_id = :uid
WHERE (DATE(t.fecha_solicitud) = :d
OR t.fecha_vencimiento = :d2
OR t.fecha_termino = :d3)
AND (t.creado_por = :email OR ua.id = :uid2 OR ti.user_id IS NOT NULL)
ORDER BY t.id DESC"
);
$tra->execute([
':uid' => $myId, ':uid2' => $myId, ':email' => $u['email'],
':d' => $today, ':d2' => $today, ':d3' => $today,
]);
}
$traRows = $tra->fetchAll();
// Expandir trámites en 1..N entradas (una por fecha que coincida hoy)
$traEvents = [];
foreach ($traRows as $t) {
$title = $t['nombre'] ?: $t['tipo_tramite'] ?: $t['tipo_proceso'] ?: 'Trámite';
if (substr((string)$t['fecha_solicitud'], 0, 10) === $today) {
$traEvents[] = $t + ['evento' => 'solicitud', 'evento_label' => 'Solicitud', 'title' => $title];
}
if (($t['fecha_vencimiento'] ?? '') === $today) {
$traEvents[] = $t + ['evento' => 'vencimiento', 'evento_label' => 'Vencimiento', 'title' => $title];
}
if (($t['fecha_termino'] ?? '') === $today) {
$traEvents[] = $t + ['evento' => 'termino', 'evento_label' => 'Término procesal', 'title' => $title];
}
}
return [
'manual' => $manualRows,
'escrituras' => $escRows,
'tramites' => $traEvents,
];
} catch (Throwable $e) {
error_log('todays_events error: ' . $e->getMessage());
return ['manual' => [], 'escrituras' => [], 'tramites' => []];
}
}
function fmt_hour(?string $t): string {
if (!$t) return '';
return substr($t, 0, 5);
}
$todayEvents = $authed
? todays_events($user, Auth::userId(), Auth::isSocio())
: ['manual' => [], 'escrituras' => [], 'tramites' => []];
$hasToday = $authed && (
!empty($todayEvents['manual']) || !empty($todayEvents['escrituras']) || !empty($todayEvents['tramites'])
);
$DEPT_LABELS = ['adm' => 'Admin', 'corp' => 'Corp', 'mig' => 'Migración', 'lit' => 'Litigios'];
$DEPT_COLORS = ['adm' => '#6c5b95', 'corp' => '#9E7C2E', 'mig' => '#2980b9', 'lit' => '#722f37'];
$TIPO_LABELS = ['reserva_sala' => 'Reserva de sala', 'cumpleanos' => 'Cumpleaños', 'recordatorio' => 'Recordatorio', 'evento' => 'Evento'];
$TIPO_COLORS = ['reserva_sala' => '#2e7d8f', 'cumpleanos' => '#c2185b', 'recordatorio' => '#e67e22', 'evento' => '#27ae60'];
function initials(string $name): string {
$parts = preg_split('/\s+/u', trim($name));
$a = mb_substr($parts[0] ?? '', 0, 1);
@@ -136,6 +238,23 @@ a{color:inherit;text-decoration:none;}
.dash-foot{text-align:center;padding:24px;font-size:11px;color:var(--gray-400);}
.dash-foot a{color:var(--navy);border-bottom:1px dotted var(--gray-400);}
/* ───────── Today's events ───────── */
.today-wrap{margin-top:48px;}
.today-list{display:flex;flex-direction:column;gap:10px;}
.today-empty{padding:18px 22px;background:#fff;border:1px dashed var(--gray-200);border-radius:12px;color:var(--gray-400);font-size:13.5px;text-align:center;}
.today-card{background:#fff;border:1px solid var(--gray-100);border-radius:12px;padding:14px 18px;display:grid;grid-template-columns:auto 1fr auto;gap:16px;align-items:center;box-shadow:var(--shadow);transition:all .15s;color:inherit;}
.today-card.linkable{cursor:pointer;}
.today-card.linkable:hover{transform:translateX(3px);border-color:var(--navy-l);box-shadow:0 6px 18px rgba(17,36,70,0.08);}
.today-bar{width:5px;height:46px;border-radius:3px;flex-shrink:0;}
.today-body{min-width:0;}
.today-title{font-family:'DM Serif Display',serif;font-size:17px;color:var(--navy);line-height:1.25;font-weight:400;}
.today-meta{font-size:12.5px;color:var(--gray-700);margin-top:4px;display:flex;gap:10px;flex-wrap:wrap;align-items:center;}
.today-meta .dot{width:3px;height:3px;border-radius:50%;background:var(--gray-400);display:inline-block;}
.today-tags{display:flex;gap:6px;flex-wrap:wrap;justify-content:flex-end;align-items:center;}
.today-tag{font-size:10.5px;font-weight:600;padding:3px 9px;border-radius:20px;color:#fff;letter-spacing:.04em;text-transform:uppercase;white-space:nowrap;}
.today-time{font-size:13px;font-weight:600;color:var(--navy);font-variant-numeric:tabular-nums;white-space:nowrap;}
.today-code{font-family:'DM Mono','SF Mono',Menlo,monospace;font-size:11px;color:var(--gray-400);letter-spacing:.04em;}
</style>
</head>
<body>
@@ -248,6 +367,92 @@ a{color:inherit;text-decoration:none;}
</a>
<?php endforeach; ?>
</div>
<?php
$meses = ['enero','febrero','marzo','abril','mayo','junio','julio','agosto','septiembre','octubre','noviembre','diciembre'];
$dias = ['domingo','lunes','martes','miércoles','jueves','viernes','sábado'];
$ts = strtotime(date('Y-m-d'));
$hoyFmt = $dias[(int)date('w', $ts)] . ' ' . (int)date('j', $ts) . ' de ' . $meses[(int)date('n', $ts) - 1];
?>
<div class="today-wrap">
<div class="section-label">Eventos de hoy · <?= htmlspecialchars($hoyFmt) ?></div>
<div class="today-list">
<?php if (!$hasToday): ?>
<div class="today-empty">No hay eventos programados para hoy.</div>
<?php else: ?>
<?php foreach ($todayEvents['manual'] as $m):
$tipo = $m['tipo'] ?: 'evento';
$color = $TIPO_COLORS[$tipo] ?? '#27ae60';
$tipoLabel = $TIPO_LABELS[$tipo] ?? 'Evento';
$time = '';
if ($m['hora_inicio']) {
$time = fmt_hour($m['hora_inicio']);
if ($m['hora_fin']) $time .= ' – ' . fmt_hour($m['hora_fin']);
} else { $time = 'Todo el día'; }
$metaBits = [htmlspecialchars($m['creado_por_nombre'] ?: $m['creado_por'])];
if ($m['ubicacion']) $metaBits[] = htmlspecialchars($m['ubicacion']);
?>
<a class="today-card linkable" href="/calendario/">
<div class="today-bar" style="background:<?= $color ?>"></div>
<div class="today-body">
<div class="today-title"><?= htmlspecialchars($m['titulo']) ?></div>
<div class="today-meta">
<span class="today-time"><?= htmlspecialchars($time) ?></span>
<?php foreach ($metaBits as $i => $b): ?>
<span class="dot"></span><span><?= $b ?></span>
<?php endforeach; ?>
</div>
</div>
<div class="today-tags">
<span class="today-tag" style="background:<?= $color ?>"><?= htmlspecialchars($tipoLabel) ?></span>
</div>
</a>
<?php endforeach; ?>
<?php foreach ($todayEvents['escrituras'] as $e): ?>
<a class="today-card linkable" href="/escrituras/?edit=<?= (int)$e['id'] ?>">
<div class="today-bar" style="background:#1B3A6B"></div>
<div class="today-body">
<div class="today-title"><?= htmlspecialchars($e['tramite'] ?: 'Escritura sin nombre') ?></div>
<div class="today-meta">
<span><?= htmlspecialchars($e['cliente'] ?: '—') ?></span>
<span class="dot"></span><span><?= htmlspecialchars($e['abogado'] ?: '—') ?></span>
<?php if ($e['estatus']): ?><span class="dot"></span><span><?= htmlspecialchars($e['estatus']) ?></span><?php endif; ?>
</div>
</div>
<div class="today-tags">
<span class="today-tag" style="background:#1B3A6B">Escritura</span>
<span class="today-tag" style="background:var(--gray-400)">Solicitud</span>
</div>
</a>
<?php endforeach; ?>
<?php foreach ($todayEvents['tramites'] as $t):
$dept = $t['departamento'];
$color = $DEPT_COLORS[$dept] ?? '#666';
$dLab = $DEPT_LABELS[$dept] ?? $dept;
?>
<a class="today-card linkable" href="/tramites/?edit=<?= urlencode($t['codigo']) ?>">
<div class="today-bar" style="background:<?= $color ?>"></div>
<div class="today-body">
<div class="today-title"><?= htmlspecialchars($t['title']) ?> <span class="today-code">· <?= htmlspecialchars($t['codigo']) ?></span></div>
<div class="today-meta">
<span><?= htmlspecialchars($t['cliente'] ?: '—') ?></span>
<span class="dot"></span><span><?= htmlspecialchars($t['abogado'] ?: '—') ?></span>
<?php if ($t['estatus']): ?><span class="dot"></span><span><?= htmlspecialchars($t['estatus']) ?></span><?php endif; ?>
</div>
</div>
<div class="today-tags">
<span class="today-tag" style="background:<?= $color ?>"><?= htmlspecialchars($dLab) ?></span>
<span class="today-tag" style="background:var(--gray-400)"><?= htmlspecialchars($t['evento_label']) ?></span>
</div>
</a>
<?php endforeach; ?>
<?php endif; ?>
</div>
</div>
</main>
<footer class="dash-foot">