NUEVA APP /horas/
* public/horas/index.php — Bootstrap::inject del view
* public/horas/api.php — endpoint GET ?action=list que retorna:
- entries: entradas detalladas con join a tramites_legales (link,
título, cliente, departamento, estatus)
- by_user: agregado por persona (solo socio/admin)
- avail_users: dropdown de usuarios para filtrar (solo socio/admin)
- total_hrs, is_socio, my_name
Filtros: ?from=YYYY-MM-DD, ?to=YYYY-MM-DD, ?user=ID
* views/horas.html — UI con:
- Header estandarizado (Portal, sync, user-chip, gear si socio, logout)
- Toolbar de filtros: rango de fechas + dropdown usuario (solo socio)
+ atajos rápidos (Hoy / Semana / Mes / Año / Todo)
- KPIs: Total horas, Entradas, Personas con horas (socio)
- Tarjetas "Por persona" (solo socio), click filtra por ese usuario
- Tabla detalle con link al trámite (?edit=CODIGO) + departamento +
horas en h:mm + descripción
VISIBILIDAD
* Abogado/usuario ve solo sus horas (match por h.abogado = mi_name).
* Socio/admin ve todas las horas. Puede filtrar por persona.
* Admin oculto del dropdown de personas (queries excluyen role=admin).
ICONO DE RELOJ EN HEADERS
* Agregado .btn-clock (alias visual de .btn-gear) en:
escrituras.html, tramites.html, calendario.html, admin.html,
horas.html, public/index.php (landing).
* Posición: al lado del user-chip, antes del gear/logout.
* Visible para todos los autenticados (no restringido por rol).
259 lines
17 KiB
PHP
259 lines
17 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
require_once __DIR__ . '/../src/Auth.php';
|
|
require_once __DIR__ . '/../src/Apps.php';
|
|
|
|
$authed = Auth::isAuthenticated();
|
|
$user = $authed ? Auth::user() : ['email' => '', 'name' => ''];
|
|
$apps = $authed ? Apps::catalog() : [];
|
|
|
|
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);}
|
|
</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 & 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>
|
|
</main>
|
|
|
|
<footer class="dash-foot">
|
|
Donna · AALN Law — <a href="https://www.aalnlaw.com" target="_blank" rel="noopener">aalnlaw.com</a>
|
|
</footer>
|
|
<?php endif; ?>
|
|
</body>
|
|
</html>
|