<?php declare(strict_types=1); if (!defined('DASHBOARD_ROOT')) { http_response_code(403); exit('Direct access denied.'); } class UI { public static function e(?string $value): string { return htmlspecialchars($value ?? '', ENT_QUOTES | ENT_HTML5, 'UTF-8'); } public static function table(array $headers, array $rows, string $id = '', string $class = ''): string { $tableId = $id ? ' id="' . self::e($id) . '"' : ''; $tableCls = $class ? ' class="' . self::e($class) . '"' : ' class="data-table"'; $html = "<table{$tableId}{$tableCls}><thead><tr>"; foreach ($headers as $h) { $html .= '<th>' . self::e($h) . '</th>'; } $html .= '</tr></thead><tbody>'; foreach ($rows as $row) { $html .= '<tr>'; foreach ($row as $cell) { $html .= '<td>' . ($cell ?? '') . '</td>'; } $html .= '</tr>'; } $html .= '</tbody></table>'; return $html; } public static function renderIcon(string $iconName, string $class = ''): string { if (!$iconName) return ''; $path = DASHBOARD_ROOT . '/assets/icons/' . $iconName . '.svg'; if (!file_exists($path)) { return '<span class="icon-placeholder ' . self::e($class) . '"></span>'; } $svg = file_get_contents($path); if ($class) { $svg = str_replace('<svg ', '<svg class="' . self::e($class) . '" ', $svg); } return $svg; } public static function kpiCard(string $label, string $value, string $icon = '', string $trend = ''): string { $trendHtml = $trend ? '<span class="kpi-trend">' . self::e($trend) . '</span>' : ''; $iconHtml = $icon ? self::renderIcon($icon, 'kpi-icon') : ''; return '<div class="kpi-card">' . $iconHtml . '<div class="kpi-value">' . self::e($value) . '</div>' . '<div class="kpi-label">' . self::e($label) . '</div>' . $trendHtml . '</div>'; } public static function badge(string $text, string $variant = 'default'): string { return '<span class="badge badge-' . self::e($variant) . '">' . self::e($text) . '</span>'; } public static function alert(string $message, string $type = 'info'): string { return '<div class="alert alert-' . self::e($type) . '">' . self::e($message) . '</div>'; } public static function pagination(int $currentPage, int $totalPages, string $baseUrl): string { if ($totalPages <= 1) return ''; $html = '<div class="pagination">'; if ($currentPage > 1) { $html .= '<a href="' . self::e($baseUrl) . '&page=' . ($currentPage - 1) . '" class="page-prev">‹</a>'; } for ($i = max(1, $currentPage - 2); $i <= min($totalPages, $currentPage + 2); $i++) { $active = $i === $currentPage ? ' class="page-active"' : ''; $html .= '<a href="' . self::e($baseUrl) . '&page=' . $i . '"' . $active . '>' . $i . '</a>'; } if ($currentPage < $totalPages) { $html .= '<a href="' . self::e($baseUrl) . '&page=' . ($currentPage + 1) . '" class="page-next">›</a>'; } $html .= '</div>'; return $html; } public static function modal(string $id, string $title, string $bodyHtml, string $footerHtml = ''): string { return '<div id="' . self::e($id) . '" class="modal-overlay" style="display:none;">' . '<div class="modal-container">' . '<div class="modal-header"><h3>' . self::e($title) . '</h3><button class="modal-close" data-dismiss="' . self::e($id) . '">×</button></div>' . '<div class="modal-body">' . $bodyHtml . '</div>' . ($footerHtml ? '<div class="modal-footer">' . $footerHtml . '</div>' : '') . '</div></div>'; } public static function field(string $name, string $label, string $type = 'text', string $value = '', array $attrs = []): string { $attrStr = ''; foreach ($attrs as $k => $v) { $attrStr .= ' ' . self::e($k) . '="' . self::e($v) . '"'; } return '<div class="form-group">' . '<label for="' . self::e($name) . '">' . self::e($label) . '</label>' . '<input type="' . self::e($type) . '" id="' . self::e($name) . '" name="' . self::e($name) . '" value="' . self::e($value) . '"' . $attrStr . '>' . '</div>'; } } 