feat: UI/UX overhaul — shared layout, SSE live feed, responsive nav, approval drawer
- admin/Layout.php: nueva clase compartida con topbar, nav agrupado y hamburger móvil - assets/app.css: CSS centralizado (~130 líneas), elimina ~2000 líneas duplicadas - DashboardController: 11 vistas refactorizadas con Layout; tarjetas de stats clicables; drawer de aprobación rápida desde el dashboard; Live Feed reemplaza polling 3s por SSE; bot-config con preview WhatsApp en tiempo real; chat con back-button móvil y textarea auto-resize - public/index.php: rutas SSE (/admin/webhook/stream/sse) y pending-list (/admin/pending-list) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
daf1947f13
commit
ef6e9c1f70
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
class Layout
|
||||
{
|
||||
private static function h(string $v): string
|
||||
{
|
||||
return htmlspecialchars($v, ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
|
||||
public static function open(
|
||||
string $title,
|
||||
string $active,
|
||||
string $userName,
|
||||
int $pending = 0
|
||||
): string {
|
||||
$t = self::h($title);
|
||||
$u = self::h($userName);
|
||||
|
||||
$groups = [
|
||||
[
|
||||
['dashboard', '/admin/dashboard', '📊', 'Dashboard'],
|
||||
['live', '/admin/live', '🔴', 'Live'],
|
||||
['chat', '/admin/chat', '💬', 'Chat'],
|
||||
],
|
||||
[
|
||||
['pending', '/admin/pending', '⏳', 'Pendientes', $pending],
|
||||
['companies', '/admin/companies', '🏢', 'Empresas'],
|
||||
],
|
||||
[
|
||||
['bot-config', '/admin/bot-config', '🤖', 'Bot'],
|
||||
['conv-flow', '/admin/conversation-flow', '🔗', 'Flujos'],
|
||||
['settings', '/admin/settings', '⚙️', 'Ajustes'],
|
||||
],
|
||||
];
|
||||
|
||||
$navHtml = '';
|
||||
foreach ($groups as $gi => $group) {
|
||||
if ($gi > 0) {
|
||||
$navHtml .= '<span class="nav-sep"></span>';
|
||||
}
|
||||
foreach ($group as $item) {
|
||||
[$key, $href, $icon, $label] = $item;
|
||||
$badge = isset($item[4]) && $item[4] > 0
|
||||
? '<span class="nav-badge">' . (int)$item[4] . '</span>'
|
||||
: '';
|
||||
$cls = $active === $key ? ' active' : '';
|
||||
$navHtml .= "<a href=\"{$href}\" class=\"nav-link{$cls}\">{$icon} {$label}{$badge}</a>";
|
||||
}
|
||||
}
|
||||
|
||||
$navHtml .= '<span class="nav-sep"></span>';
|
||||
$navHtml .= '<a href="/admin/sync-companies" class="nav-link nav-action" title="Sincronizar desde ERP" onclick="return confirm(\'¿Sincronizar empresas desde el ERP?\')">🔄</a>';
|
||||
$navHtml .= '<a href="/admin/process-queue" class="nav-link nav-action" title="Procesar cola de mensajes" onclick="return confirm(\'¿Procesar cola de mensajes?\')">📨</a>';
|
||||
|
||||
return <<<HTML
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{$t} — Palmas360</title>
|
||||
<link rel="stylesheet" href="/assets/app.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="topbar">
|
||||
<div style="display:flex;align-items:center;gap:10px">
|
||||
<button class="nav-hamburger" id="navToggle" onclick="document.getElementById('navBar').classList.toggle('open')" aria-label="Menu">☰</button>
|
||||
<h1><img src="https://app.palmas360.com/Test/imgLogin/logo.png" alt="Palmas360" style="height:32px;vertical-align:middle;margin-right:8px;filter:brightness(0) invert(1)"> {$t}</h1>
|
||||
</div>
|
||||
<div class="topbar-r">
|
||||
<span class="ubadge">👤 {$u}</span>
|
||||
<a href="/logout" class="btn-out">Salir</a>
|
||||
</div>
|
||||
</div>
|
||||
<nav class="nav-bar" id="navBar">
|
||||
{$navHtml}
|
||||
</nav>
|
||||
HTML;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user