diff --git a/CHANGELOG.md b/CHANGELOG.md index c276e9c..9885eb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## [1.21.0] - 2026-10-26 + +### Added +- **Sub-pestaña Genérica de Historial y Registros en Plugins (`PluginManifest`, `PluginSettingsComponent`)**: + - Incorporado soporte en `PluginManifest` para `recordsEndpoint` y `recordsColumns` (`RecordColumn`), permitiendo a cualquier plugin declarar un origen de datos tabular desacoplado. + - Añadido selector de sub-pestañas en `plugin-settings` para alternar entre "Configuración" e "Historial y Registros". + - Vista de registros completa con buscador en tiempo real (por DNI, puesto o aula), ordenación cronológica, paginación reactiva y contador de resultados. + - Soporte de exportación a archivo CSV con descarga directa desde backend (`${recordsEndpoint}/export`) y fallback automático de generación en cliente con BOM UTF-8 para compatibilidad universal. + - Renderizado adaptativo de celdas con soporte de badges semánticos para identificadores (DNI), duraciones y motivos de cierre de sesión (`TIMEOUT`, `RELEASED_BY_CONCIERGE`, `DISABLED_BY_ADMIN`). + - Total adhesión a la sintaxis moderna de control de flujo Angular (`@if`, `@for`, `@switch`) y estilos reutilizables sin atributos inline. + ## [1.20.0] - 2026-10-26 ### Added diff --git a/ogWebconsole/src/app/components/plugins/plugin-settings/plugin-settings.component.css b/ogWebconsole/src/app/components/plugins/plugin-settings/plugin-settings.component.css index a0dd8b8..5f429af 100644 --- a/ogWebconsole/src/app/components/plugins/plugin-settings/plugin-settings.component.css +++ b/ogWebconsole/src/app/components/plugins/plugin-settings/plugin-settings.component.css @@ -514,3 +514,402 @@ grid-template-columns: 1fr; } } + +/* ========================================================================= + BARRA DE SUB-PESTAÑAS (Configuración vs Historial / Registros) + ========================================================================= */ + +.subtabs-navigation-bar { + display: flex; + align-items: center; + gap: 8px; + background: #f1f5f9; + border: 1px solid #e2e8f0; + border-radius: 10px; + padding: 4px; + width: fit-content; +} + +.subtab-nav-item { + display: flex; + align-items: center; + gap: 8px; + padding: 8px 20px; + border-radius: 8px; + font-size: 0.92rem; + font-weight: 600; + border: none; + background: transparent; + color: #64748b; + cursor: pointer; + transition: all 0.2s ease; +} + +.subtab-nav-item:hover { + color: #0f172a; + background: rgba(255, 255, 255, 0.6); +} + +.subtab-nav-item.active { + background: #ffffff; + color: #0284c7; + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06); +} + +.subtab-nav-icon { + font-size: 20px; + width: 20px; + height: 20px; +} + +.subtab-counter-badge { + font-size: 0.72rem; + font-weight: 700; + padding: 2px 8px; + border-radius: 12px; + background: #e0f2fe; + color: #0369a1; +} + +/* ========================================================================= + VISTA DE HISTORIAL Y REGISTROS TABULARES + ========================================================================= */ + +.records-wrapper { + background: #ffffff; + border: 1px solid #e2e8f0; + border-radius: 12px; + padding: 24px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.02); + display: flex; + flex-direction: column; + gap: 16px; +} + +.records-header-row { + display: flex; + justify-content: space-between; + align-items: center; + flex-wrap: wrap; + gap: 16px; + padding-bottom: 8px; +} + +.records-title-group { + display: flex; + align-items: center; + gap: 12px; +} + +.records-header-icon { + font-size: 32px; + width: 32px; + height: 32px; + color: #0284c7; +} + +.records-heading { + margin: 0; + font-size: 1.15rem; + font-weight: 600; + color: #0f172a; +} + +.records-subheading { + margin: 4px 0 0 0; + color: #64748b; + font-size: 0.85rem; +} + +.records-actions-group { + display: flex; + align-items: center; + gap: 10px; + flex-wrap: wrap; +} + +.records-search-box { + position: relative; + display: flex; + align-items: center; + background: #f8fafc; + border: 1px solid #cbd5e1; + border-radius: 8px; + padding: 0 10px; + height: 38px; + transition: border-color 0.2s, box-shadow 0.2s; +} + +.records-search-box:focus-within { + border-color: #0284c7; + box-shadow: 0 0 0 2px rgba(2, 132, 199, 0.15); +} + +.records-search-box .search-icon { + font-size: 18px; + width: 18px; + height: 18px; + color: #94a3b8; + margin-right: 6px; +} + +.records-search-input { + border: none; + background: transparent; + outline: none; + font-size: 0.88rem; + width: 240px; + color: #1e293b; +} + +.clear-search-btn { + border: none; + background: transparent; + cursor: pointer; + padding: 0; + color: #94a3b8; + display: flex; + align-items: center; + justify-content: center; +} + +.clear-search-btn:hover { + color: #475569; +} + +.clear-icon { + font-size: 16px; + width: 16px; + height: 16px; +} + +.btn-records-action { + display: flex; + align-items: center; + gap: 6px; + height: 38px; + font-size: 0.85rem; + font-weight: 500; + border-radius: 8px; +} + +.btn-icon { + font-size: 18px; + width: 18px; + height: 18px; +} + +.btn-icon.spinning { + animation: spin 1s linear infinite; +} + +/* ========================================================================= + TABLA DE REGISTROS + ========================================================================= */ + +.records-table-container { + display: flex; + flex-direction: column; + width: 100%; +} + +.table-responsive { + width: 100%; + overflow-x: auto; + border: 1px solid #e2e8f0; + border-radius: 8px; +} + +.records-data-table { + width: 100%; + border-collapse: collapse; + font-size: 0.9rem; + text-align: left; +} + +.records-data-table thead tr { + background: #f8fafc; + border-bottom: 2px solid #e2e8f0; +} + +.records-data-table th { + padding: 12px 16px; + font-weight: 600; + color: #475569; + font-size: 0.82rem; + text-transform: uppercase; + letter-spacing: 0.5px; + white-space: nowrap; +} + +.records-data-table tbody tr { + border-bottom: 1px solid #f1f5f9; + transition: background-color 0.15s ease; +} + +.records-data-table tbody tr:last-child { + border-bottom: none; +} + +.records-data-table tbody tr:hover { + background-color: #f8fafc; +} + +.records-data-table td { + padding: 12px 16px; + color: #1e293b; + vertical-align: middle; + white-space: nowrap; +} + +.badge-dni { + font-family: 'Consolas', 'Fira Code', 'Monaco', monospace; + font-weight: 700; + background: #f1f5f9; + color: #0f172a; + padding: 3px 8px; + border-radius: 6px; + border: 1px solid #e2e8f0; + font-size: 0.85rem; +} + +.badge-duration { + font-weight: 600; + color: #475569; + font-size: 0.85rem; +} + +.badge-reason { + display: inline-block; + font-size: 0.78rem; + font-weight: 600; + padding: 3px 10px; + border-radius: 12px; +} + +.reason-timeout { + background: #fef3c7; + color: #92400e; +} + +.reason-concierge { + background: #e0f2fe; + color: #0369a1; +} + +.reason-disabled { + background: #fee2e2; + color: #991b1b; +} + +.reason-default { + background: #f1f5f9; + color: #475569; +} + +/* ========================================================================= + PAGINACIÓN + ========================================================================= */ + +.records-pagination-bar { + display: flex; + justify-content: space-between; + align-items: center; + padding-top: 16px; + border-top: 1px solid #f1f5f9; + flex-wrap: wrap; + gap: 12px; +} + +.pagination-info { + font-size: 0.85rem; + color: #64748b; +} + +.pagination-controls { + display: flex; + align-items: center; + gap: 12px; +} + +.page-btn { + display: flex; + align-items: center; + gap: 4px; + background: #ffffff; + border: 1px solid #cbd5e1; + border-radius: 6px; + padding: 6px 14px; + font-size: 0.82rem; + font-weight: 500; + color: #334155; + cursor: pointer; + transition: all 0.2s ease; +} + +.page-btn:hover:not(:disabled) { + background: #f8fafc; + border-color: #94a3b8; + color: #0f172a; +} + +.page-btn:disabled { + opacity: 0.45; + cursor: not-allowed; +} + +.page-current { + font-size: 0.85rem; + font-weight: 600; + color: #0f172a; +} + +/* ========================================================================= + ESTADOS DE CARGA Y VACÍO + ========================================================================= */ + +.records-loading-state, +.records-empty-state { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 60px 20px; + gap: 12px; + text-align: center; +} + +.loading-spinner { + width: 34px; + height: 34px; + border: 3px solid #e2e8f0; + border-top: 3px solid #0284c7; + border-radius: 50%; + animation: spin 0.8s linear infinite; +} + +.empty-state-icon { + font-size: 48px; + width: 48px; + height: 48px; + color: #cbd5e1; +} + +.empty-state-title { + margin: 0; + font-size: 1.05rem; + font-weight: 600; + color: #334155; +} + +.empty-state-text { + margin: 0; + font-size: 0.88rem; + color: #64748b; + max-width: 420px; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} diff --git a/ogWebconsole/src/app/components/plugins/plugin-settings/plugin-settings.component.html b/ogWebconsole/src/app/components/plugins/plugin-settings/plugin-settings.component.html index 9e3ef77..aa31f86 100644 --- a/ogWebconsole/src/app/components/plugins/plugin-settings/plugin-settings.component.html +++ b/ogWebconsole/src/app/components/plugins/plugin-settings/plugin-settings.component.html @@ -24,129 +24,283 @@ @if (plugin.enabled) { -
- Ajusta el comportamiento y las opciones de ejecución. Los cambios se aplicarán inmediatamente a las nuevas órdenes enviadas a los puestos. -
- -Registro cronológico de sesiones y eventos gestionados por {{ plugin.titleKey ? (plugin.titleKey | translate) : plugin.title }}
+Cargando registros...
++ @if (recordsSearch) { + No se encontraron resultados para la búsqueda "{{ recordsSearch }}". + } @else { + Aún no se ha generado ningún registro o actividad en este plugin. + } +
+| {{ col.labelKey ? (col.labelKey | translate) : col.label }} | + } +
|---|
| + @switch (col.key) { + @case ('dni') { + {{ row[col.key] || '-' }} + } + @case ('end_reason') { + + {{ formatReason(row[col.key]) }} + + } + @case ('duration_minutes') { + {{ row[col.key] != null ? row[col.key] + ' min' : '-' }} + } + @default { + {{ row[col.key] ?? '-' }} + } + } + | + } +