Files
donna/public/index.php
T
Soporte ZH Consultores d75a2a5fda 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
2026-06-04 21:05:28 -05:00

464 lines
27 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?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);
$b = mb_substr($parts[1] ?? '', 0, 1);
return strtoupper($a . $b) ?: 'AA';
}
function icon(string $name): string {
$svgs = [
'document' => '<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="9" y1="13" x2="15" y2="13"/><line x1="9" y1="17" x2="15" y2="17"/>',
'briefcase' => '<rect x="2" y="7" width="20" height="14" rx="2"/><path d="M16 7V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v2"/>',
'calendar' => '<rect x="3" y="4" width="18" height="18" rx="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/>',
'users' => '<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/>',
];
return $svgs[$name] ?? $svgs['document'];
}
?><!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?= $authed ? 'Donna · AALN Portal' : 'Donna · AALN Law' ?></title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=DM+Serif+Display&family=DM+Sans:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<style>
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0;}
:root{
--navy:#1B3A6B;
--navy-d:#112446;
--navy-l:#2D5090;
--gold:#C9A84C;
--gold-l:#E8C96A;
--gold-d:#9E7C2E;
--off:#F7F6F3;
--gray-100:#EEEDE9;
--gray-200:#D6D4CC;
--gray-400:#9B9A96;
--gray-700:#4A4946;
--gray-900:#1A1916;
--shadow:0 1px 3px rgba(0,0,0,0.06),0 4px 16px rgba(0,0,0,0.06);
--shadow-lg:0 12px 40px rgba(17,36,70,0.18);
}
html,body{min-height:100%;}
body{font-family:'DM Sans',system-ui,sans-serif;color:var(--gray-900);background:var(--off);font-size:15px;-webkit-font-smoothing:antialiased;}
a{color:inherit;text-decoration:none;}
/* ───────── LANDING (anonymous) ───────── */
.landing{min-height:100vh;display:flex;flex-direction:column;background:linear-gradient(135deg,#0E1F3F 0%,#1B3A6B 55%,#2D5090 100%);color:#fff;position:relative;overflow:hidden;}
.landing::before{content:'';position:absolute;inset:0;background:radial-gradient(ellipse 60% 50% at 80% 10%,rgba(201,168,76,0.22),transparent 60%),radial-gradient(ellipse 40% 40% at 10% 90%,rgba(201,168,76,0.12),transparent 60%);pointer-events:none;}
.landing-nav{padding:28px 48px;display:flex;align-items:center;justify-content:space-between;position:relative;z-index:2;}
.brand{display:flex;align-items:center;gap:14px;}
.brand-mark{width:42px;height:42px;background:var(--gold);border-radius:8px;display:flex;align-items:center;justify-content:center;font-family:'DM Serif Display',serif;font-size:20px;color:var(--navy);}
.brand-text{display:flex;flex-direction:column;line-height:1.15;}
.brand-firm{font-size:11px;font-weight:600;color:var(--gold);letter-spacing:.18em;text-transform:uppercase;}
.brand-firm a{color:inherit;border-bottom:1px dotted transparent;transition:border-color .2s;}
.brand-firm a:hover{border-color:var(--gold);}
.brand-name{font-family:'DM Serif Display',serif;font-size:22px;color:#fff;}
.landing-foot-link{font-size:12px;color:rgba(255,255,255,0.55);letter-spacing:.06em;}
.landing-foot-link a{color:rgba(255,255,255,0.8);border-bottom:1px solid rgba(255,255,255,0.2);padding-bottom:1px;}
.landing-foot-link a:hover{color:var(--gold);border-color:var(--gold);}
.hero{flex:1;display:grid;grid-template-columns:1.1fr 1fr;gap:48px;padding:40px 80px 80px;align-items:center;position:relative;z-index:2;}
@media (max-width:980px){.hero{grid-template-columns:1fr;padding:24px 32px 48px;}}
.hero-text{max-width:560px;}
.hero-eyebrow{font-size:12px;font-weight:600;color:var(--gold);letter-spacing:.22em;text-transform:uppercase;margin-bottom:24px;display:flex;align-items:center;gap:12px;}
.hero-eyebrow::before{content:'';width:32px;height:1px;background:var(--gold);display:inline-block;}
.hero-title{font-family:'DM Serif Display',serif;font-size:clamp(40px,5.6vw,68px);line-height:1.05;color:#fff;margin-bottom:24px;font-weight:400;}
.hero-title em{font-style:italic;color:var(--gold-l);}
.hero-sub{font-size:17px;line-height:1.6;color:rgba(255,255,255,0.78);margin-bottom:36px;font-weight:300;max-width:520px;}
.btn-login{display:inline-flex;align-items:center;gap:12px;background:var(--gold);color:var(--navy-d);padding:16px 32px;border-radius:10px;font-weight:600;font-size:15px;letter-spacing:.02em;border:none;cursor:pointer;transition:transform .15s,box-shadow .15s,background .15s;box-shadow:0 6px 18px rgba(201,168,76,0.35);}
.btn-login:hover{background:var(--gold-l);transform:translateY(-1px);box-shadow:0 10px 24px rgba(201,168,76,0.42);}
.btn-login:active{transform:translateY(0);}
.btn-login svg{width:18px;height:18px;}
.login-meta{margin-top:18px;font-size:12px;color:rgba(255,255,255,0.5);letter-spacing:.04em;}
.hero-art{position:relative;height:420px;display:flex;align-items:center;justify-content:center;}
@media (max-width:980px){.hero-art{display:none;}}
.card-stack{position:relative;width:100%;max-width:420px;}
.art-card{position:absolute;background:rgba(255,255,255,0.06);border:1px solid rgba(201,168,76,0.25);backdrop-filter:blur(14px);border-radius:14px;padding:22px;color:#fff;box-shadow:var(--shadow-lg);}
.art-card-1{top:0;left:0;width:280px;transform:rotate(-3deg);}
.art-card-2{top:90px;right:0;width:300px;transform:rotate(2deg);}
.art-card-3{bottom:0;left:40px;width:280px;transform:rotate(-1deg);}
.art-card-eyebrow{font-size:9.5px;color:var(--gold);letter-spacing:.16em;text-transform:uppercase;font-weight:600;margin-bottom:10px;}
.art-card h4{font-family:'DM Serif Display',serif;font-size:16px;font-weight:400;line-height:1.3;margin-bottom:8px;color:#fff;}
.art-card p{font-size:12px;color:rgba(255,255,255,0.65);line-height:1.5;}
.art-card .pill{display:inline-block;font-size:10px;font-weight:600;padding:2px 8px;border-radius:20px;background:rgba(201,168,76,0.2);color:var(--gold-l);margin-top:8px;letter-spacing:.04em;}
/* ───────── DASHBOARD (authenticated) ───────── */
.dash-header{background:var(--navy);border-bottom:3px solid var(--gold);padding:0 32px;display:flex;align-items:center;justify-content:space-between;height:64px;position:sticky;top:0;z-index:200;}
.dash-header .brand-mark{width:36px;height:36px;font-size:16px;}
.dash-header .brand-name{font-size:18px;}
.user-chip{display:flex;align-items:center;gap:10px;background:rgba(255,255,255,0.08);border:1px solid rgba(255,255,255,0.12);border-radius:24px;padding:5px 6px 5px 6px;color:#fff;}
.user-avatar{width:30px;height:30px;border-radius:50%;background:var(--gold);color:var(--navy-d);display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:700;flex-shrink:0;}
.user-info{display:flex;flex-direction:column;line-height:1.15;padding-right:6px;}
.user-name{font-size:12.5px;font-weight:500;color:#fff;max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.user-email{font-size:10.5px;color:rgba(255,255,255,0.55);max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.btn-logout{font-size:11px;padding:6px 12px;border:1px solid rgba(255,255,255,0.18);border-radius:18px;background:transparent;color:rgba(255,255,255,0.7);cursor:pointer;font-family:'DM Sans',sans-serif;transition:all .15s;}
.btn-logout:hover{background:rgba(255,255,255,0.12);color:#fff;border-color:rgba(255,255,255,0.3);}
.btn-gear,.btn-clock{display:inline-flex;align-items:center;justify-content:center;width:36px;height:36px;border:1px solid rgba(255,255,255,0.18);border-radius:8px;color:rgba(255,255,255,0.85);transition:all .15s;text-decoration:none;font-family:'DM Sans',sans-serif;margin-right:8px;}
.btn-gear:hover,.btn-clock:hover{background:var(--gold);border-color:var(--gold);color:var(--navy);}
.btn-gear svg,.btn-clock svg{width:16px;height:16px;}
.dash-main{max-width:1180px;margin:0 auto;padding:48px 32px 80px;}
.dash-hero{margin-bottom:40px;}
.dash-greeting{font-family:'DM Serif Display',serif;font-size:36px;color:var(--navy);font-weight:400;line-height:1.15;margin-bottom:8px;}
.dash-greeting em{color:var(--gold-d);font-style:italic;}
.dash-sub{font-size:15px;color:var(--gray-700);max-width:560px;line-height:1.55;}
.section-label{font-size:11px;font-weight:600;color:var(--gray-400);letter-spacing:.18em;text-transform:uppercase;margin-bottom:18px;display:flex;align-items:center;gap:14px;}
.section-label::after{content:'';flex:1;height:1px;background:var(--gray-200);}
.app-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:20px;}
.app-card{background:#fff;border:1px solid var(--gray-100);border-radius:14px;padding:24px;display:flex;flex-direction:column;gap:14px;transition:all .2s;position:relative;box-shadow:var(--shadow);cursor:pointer;color:inherit;min-height:200px;}
.app-card.live:hover{transform:translateY(-3px);box-shadow:0 12px 32px rgba(17,36,70,0.10);border-color:var(--navy-l);}
.app-card.coming-soon{cursor:default;opacity:0.7;}
.app-card.coming-soon:hover{transform:none;}
.app-icon{width:48px;height:48px;border-radius:10px;background:rgba(27,58,107,0.08);color:var(--navy);display:flex;align-items:center;justify-content:center;}
.app-card.live .app-icon{background:linear-gradient(135deg,var(--navy) 0%,var(--navy-l) 100%);color:var(--gold-l);}
.app-icon svg{width:24px;height:24px;}
.app-title{font-family:'DM Serif Display',serif;font-size:22px;color:var(--navy);font-weight:400;line-height:1.2;}
.app-desc{font-size:13.5px;color:var(--gray-700);line-height:1.55;flex:1;}
.app-action{font-size:12px;font-weight:600;color:var(--gold-d);letter-spacing:.06em;text-transform:uppercase;display:flex;align-items:center;gap:6px;}
.app-action svg{width:14px;height:14px;transition:transform .2s;}
.app-card.live:hover .app-action svg{transform:translateX(4px);}
.badge-soon{position:absolute;top:18px;right:18px;font-size:10px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;padding:3px 10px;border-radius:20px;background:var(--gray-100);color:var(--gray-700);}
.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>
<?php if (!$authed): ?>
<div class="landing">
<nav class="landing-nav">
<div class="brand">
<div class="brand-mark">D</div>
<div class="brand-text">
<span class="brand-firm"><a href="https://www.aalnlaw.com" target="_blank" rel="noopener">AALN · Arias, Abrego, López &amp; Noriega</a></span>
<span class="brand-name">Donna</span>
</div>
</div>
<div class="landing-foot-link">
Portal interno · <a href="https://www.aalnlaw.com" target="_blank" rel="noopener">aalnlaw.com</a>
</div>
</nav>
<section class="hero">
<div class="hero-text">
<div class="hero-eyebrow">Plataforma interna AALN</div>
<h1 class="hero-title">Tu equipo de trabajo, <em>siempre un paso adelante</em>.</h1>
<p class="hero-sub">Donna es el portal interno de AALN: control de escrituras, expedientes, agenda y herramientas internas en un solo lugar. Inicia sesión con tu correo corporativo.</p>
<form method="get" action="/auth/login.php">
<button class="btn-login" type="submit">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/><polyline points="22,6 12,13 2,6"/></svg>
Iniciar sesión con correo corporativo
</button>
</form>
<div class="login-meta">Autenticación segura vía Mailcow · mail.aal-law.com</div>
</div>
<div class="hero-art" aria-hidden="true">
<div class="card-stack">
<div class="art-card art-card-1">
<div class="art-card-eyebrow">Trámite #1284</div>
<h4>Constitución sociedad anónima</h4>
<p>Notaría Quinta · Lic. R. Abrego</p>
<span class="pill">En Registro</span>
</div>
<div class="art-card art-card-2">
<div class="art-card-eyebrow">Hoy · 14:30</div>
<h4>Audiencia preliminar</h4>
<p>Juzgado Primero Civil · Sala 3</p>
<span class="pill">Confirmada</span>
</div>
<div class="art-card art-card-3">
<div class="art-card-eyebrow">Cliente nuevo</div>
<h4>Tech Holdings, S.A.</h4>
<p>KYC pendiente · Asignado a M. Noriega</p>
<span class="pill">Onboarding</span>
</div>
</div>
</div>
</section>
</div>
<?php else: ?>
<header class="dash-header">
<div class="brand">
<div class="brand-mark">D</div>
<div class="brand-text">
<span class="brand-firm" style="color:var(--gold);">AALN · Donna</span>
<span class="brand-name" style="font-size:14px;color:rgba(255,255,255,0.7);">Portal interno</span>
</div>
</div>
<div style="display:flex;align-items:center;">
<a class="btn-clock" href="/horas/" title="Reporte de horas">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
</a>
<?php if (Auth::isSocio()): ?>
<a class="btn-gear" href="/admin/" title="Panel de Control">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
</a>
<?php endif; ?>
<div class="user-chip">
<div class="user-avatar"><?= htmlspecialchars(initials($user['name'] ?: $user['email'])) ?></div>
<div class="user-info">
<span class="user-name"><?= htmlspecialchars($user['name'] ?: $user['email']) ?></span>
<?php if ($user['name']): ?><span class="user-email"><?= htmlspecialchars($user['email']) ?></span><?php endif; ?>
</div>
<a href="/auth/logout.php" class="btn-logout">Cerrar sesión</a>
</div>
</div>
</header>
<main class="dash-main">
<div class="dash-hero">
<h1 class="dash-greeting">Hola, <em><?= htmlspecialchars(explode(' ', $user['name'] ?: $user['email'])[0]) ?></em>.</h1>
<p class="dash-sub">Bienvenido a Donna. Elige una herramienta para empezar.</p>
</div>
<div class="section-label">Aplicaciones disponibles</div>
<div class="app-grid">
<?php foreach ($apps as $app): ?>
<?php $isLive = $app['status'] === 'live'; ?>
<a class="app-card <?= $isLive ? 'live' : 'coming-soon' ?>" href="<?= htmlspecialchars($app['href']) ?>"<?= $isLive ? '' : ' onclick="return false"' ?>>
<?php if (!$isLive): ?><span class="badge-soon">Próximamente</span><?php endif; ?>
<div class="app-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><?= icon($app['icon']) ?></svg>
</div>
<div class="app-title"><?= htmlspecialchars($app['title']) ?></div>
<div class="app-desc"><?= htmlspecialchars($app['description']) ?></div>
<?php if ($isLive): ?>
<div class="app-action">
Abrir
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/></svg>
</div>
<?php endif; ?>
</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">
Donna · AALN Law &mdash; <a href="https://www.aalnlaw.com" target="_blank" rel="noopener">aalnlaw.com</a>
</footer>
<?php endif; ?>
</body>
</html>