Files

1484 lines
92 KiB
PHP
Raw Permalink 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.
<x-app-layout>
<div class="container-fluid pb-4" id="monitoring-master">
<!-- HEADER STRIP: WITH ENTRANCE ANIMATION (SLOW) -->
<div class="card adminuiux-card bg-dark text-white mb-3 border-0 shadow-lg overflow-hidden"
>
<div class="card-body p-4 position-relative">
<div class="row align-items-center position-relative z-1">
<div class="col">
<h1 class="display-5 fw-bold text-white mb-1 tracking-tight">{{ __('Monitoring Center') }}</h1>
<p class="small text-white-50 mb-0 d-flex align-items-center gap-2">
<span class="status-indicator"></span>
System operational since <span class="badge text-bg-theme-1 rounded-pill px-3 shadow-sm"
id="stat-uptime-badge">{{ $stats['uptime'] }}</span> at node {{ $stats['hostname'] }}
</p>
</div>
</div>
<!-- Subtle Background Decorative Circles -->
<div class="bg-decoration"></div>
</div>
</div>
<!-- MAIN GRID: STAGGERED ANIMATION -->
<div class="row g-3 mb-4">
<!-- INFRASTRUCTURE FEED -->
<div class="col-lg-4">
<div class="card adminuiux-card border-0 shadow-sm h-100 hover-lift" >
<div
class="card-header bg-transparent border-0 pt-4 px-4 d-flex justify-content-between align-items-center">
<h6 class="fw-bold text-dark mb-0">{{ __('Infrastructure Feed') }}</h6>
<span class="pulse-badge success" id="global-health-pill">ONLINE</span>
</div>
<div class="card-body px-0 pt-2">
<div class="list-group list-group-flush">
<!-- REDIS -->
<div
class="list-group-item border-0 py-3 px-4 d-flex align-items-center transition-all hover-bg-light">
<div class="icon-box me-3"><i class="bi bi-lightning-charge fs-5"></i></div>
<div class="flex-grow-1 text-truncate">
<div class="small fw-bold text-dark">Redis Cache</div>
<div class="extra-small text-muted"><span
id="stat-redis-latency">{{ $stats['redis']['latency'] ?? '0ms' }}</span> |
<span id="stat-redis-memory">{{ $stats['redis']['memory_used'] ?? 'Unknown' }}</span>
</div>
</div>
<span class="fw-bold extra-small {{ ($stats['redis']['status'] ?? '') === 'connected' ? 'text-success status-pulse' : 'text-danger' }}"
id="stat-redis-status-text">{{ ($stats['redis']['status'] ?? '') === 'connected' ? 'CONNECTED' : 'DISCONNECTED' }}</span>
</div>
<!-- DB -->
<div
class="list-group-item border-0 py-3 px-4 d-flex align-items-center transition-all hover-bg-light">
<div class="icon-box me-3"><i class="bi bi-database-check fs-5"></i></div>
<div class="flex-grow-1 text-truncate">
<div class="small fw-bold text-dark">Database Engine</div>
<div class="extra-small text-muted" id="stat-db-meta">{{ $stats['db_stats']['tables'] }} Tables | {{ $stats['db_stats']['latency'] ?? '0ms' }}</div>
</div>
<span class="fw-bold extra-small status-pulse {{ ($stats['db_stats']['status'] ?? 'STABLE') === 'STABLE' ? 'text-success' : 'text-danger' }}" id="stat-db-status">
{{ $stats['db_stats']['status'] ?? 'STABLE' }}
</span>
</div>
<!-- REVERB -->
<div
class="list-group-item border-0 py-3 px-4 d-flex align-items-center transition-all hover-bg-light">
<div class="icon-box me-3 {{ $stats['has_reverb'] ? 'active' : '' }}" id="reverb-icon">
<i class="bi bi-broadcast fs-5"></i>
</div>
<div class="flex-grow-1 text-truncate">
<div class="small fw-bold text-dark">Real-time Echo</div>
<div class="extra-small text-muted">WebSocket (Reverb) active</div>
</div>
<span
class="fw-bold extra-small {{ $stats['has_reverb'] ? 'text-success status-pulse' : 'text-muted' }}"
id="reverb-status-text">
{{ $stats['has_reverb'] ? 'ACTIVE' : 'IDLE' }}
</span>
</div>
<!-- SAP -->
<div
class="list-group-item border-0 py-3 px-4 d-flex align-items-center transition-all hover-bg-light">
<div class="icon-box me-3 {{ $stats['sap']['active'] ? 'active' : '' }}">
<i class="bi bi-diagram-3 fs-5"></i>
</div>
<div class="flex-grow-1 text-truncate">
<div class="small fw-bold text-dark">SAP RFC Bridge</div>
<div class="extra-small text-muted">Trace: {{ $stats['sap']['size'] }}</div>
</div>
<span class="fw-bold extra-small {{ $stats['sap']['active'] ? 'text-success' : 'text-muted' }}">
{{ $stats['sap']['status'] }}
</span>
</div>
<!-- MOBILE -->
<div
class="list-group-item border-0 py-3 px-4 d-flex align-items-center transition-all hover-bg-light">
<div class="icon-box me-3 active">
<i class="bi bi-phone fs-5"></i>
</div>
<div class="flex-grow-1 text-truncate">
<div class="small fw-bold text-dark">Mobile Telemetry</div>
<div class="extra-small text-muted">{{ $stats['mobile']['total_logs'] }} Events recorded</div>
</div>
<span class="fw-bold extra-small text-primary">
{{ $stats['mobile']['last_activity'] }}
</span>
</div>
<!-- JOBS THROUGHPUT -->
<div
class="list-group-item border-0 py-3 px-4 d-flex align-items-center transition-all hover-bg-light">
<div class="icon-box me-3"><i class="bi bi-cpu fs-5"></i></div>
<div class="flex-grow-1 text-truncate">
<div class="small fw-bold text-dark">Job Throughput</div>
<div class="extra-small text-muted" id="stat-queues-load-text">{{ $stats['queues']['load_factor'] }} tasks/min</div>
</div>
<span class="fw-bold extra-small text-success status-pulse">RUNNING</span>
</div>
</div>
</div>
</div>
</div>
<!-- VITAL NUMBERS (MIDDLE) -->
<div class="col-lg-5">
<div class="row g-3 h-100">
<div class="col-6">
<div class="card adminuiux-card border-0 shadow-sm h-100 hover-lift">
<div class="card-body p-4">
<div class="d-flex justify-content-between mb-2">
<h6 class="fw-bold text-dark small mb-0">CPU</h6>
<i class="bi bi-speedometer text-theme-1"></i>
</div>
<h1 class="display-3 fw-black text-theme-1 mb-0 counter-value" id="stat-cpu-percent">
{{ $stats['cpu'] }}%
</h1>
<div class="mini-progress mt-2">
<div class="bar" id="cpu-bar" style="width:{{$stats['cpu']}}%"></div>
</div>
</div>
</div>
</div>
<div class="col-6">
<div class="card adminuiux-card border-0 shadow-sm h-100 hover-lift">
<div class="card-body p-4">
<div class="d-flex justify-content-between mb-2">
<h6 class="fw-bold text-dark small mb-0">RAM</h6>
<i class="bi bi-memory text-info"></i>
</div>
<h1 class="display-3 fw-black text-info mb-0 counter-value" id="stat-ram-percent">
{{ $stats['ram']['percentage'] }}%
</h1>
<div class="d-flex justify-content-between extra-small text-muted mt-1">
<span id="stat-ram-used">{{ $stats['ram']['used'] }} used</span>
<span id="stat-swap-info" class="text-warning">Swap: {{ $stats['ram']['swap']['percentage'] ?? 0 }}%</span>
</div>
</div>
</div>
</div>
<div class="col-6">
<div
class="card adminuiux-card border-0 shadow-sm h-100 hover-lift text-warning card-glow-warning">
<div class="card-body p-4">
<div class="d-flex justify-content-between mb-2">
<h6 class="fw-bold text-dark small mb-0">DISK</h6>
<i class="bi bi-hdd-network"></i>
</div>
<h1 class="display-3 fw-black mb-0 counter-value" id="stat-disk-percent">
{{ $stats['disk']['percentage'] }}%
</h1>
<p class="extra-small text-muted mb-0" id="stat-disk-total">
{{ $stats['disk']['total'] }} available
</p>
</div>
</div>
</div>
<div class="col-6">
<div
class="card adminuiux-card border-0 shadow-sm h-100 hover-lift bg-theme-1 text-white card-glow-theme">
<div class="card-body p-4">
<div class="d-flex justify-content-between mb-2">
<h6 class="fw-bold text-white small mb-0">USERS</h6>
<i class="bi bi-people text-white-50"></i>
</div>
<h1 class="display-3 fw-black text-white mb-0 counter-value" id="stat-users-count">
{{ $stats['users']['total'] }}
</h1>
<p class="extra-small text-white-50 mb-0">
<span id="stat-users-auth">{{ $stats['users']['authenticated'] }}</span> Authenticated
</p>
</div>
</div>
</div>
</div>
</div>
<!-- AUTOMATION (RIGHT) -->
<div class="col-lg-3">
<div class="row g-3 h-100">
<div class="col-12">
<div
class="card adminuiux-card border-0 shadow-sm h-100 hover-lift position-relative overflow-hidden">
<div class="card-body p-4">
<h6 class="fw-bold text-dark small">PENDING</h6>
<h1 class="display-1 fw-black text-theme-1 counter-value" id="stat-queues-pending">
{{ $stats['queues']['pending'] }}
</h1>
<i
class="bi bi-hourglass-top position-absolute top-50 end-0 translate-middle-y opacity-10 fs-1 me-3"></i>
</div>
</div>
</div>
<div class="col-12">
<div
class="card adminuiux-card border-0 shadow-sm h-100 hover-lift position-relative overflow-hidden">
<div class="card-body p-4">
<h6 class="fw-bold text-dark small text-danger">INCIDENTS</h6>
<h1 class="display-1 fw-black text-danger counter-value" id="stat-queues-failed">
{{ $stats['queues']['failed'] }}
</h1>
<i
class="bi bi-exclamation-triangle position-absolute top-50 end-0 translate-middle-y opacity-10 fs-1 me-3"></i>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- TABS AREA: MODERIZED CONSOLE -->
<div class="row g-3">
<div class="col-12">
<div class="card adminuiux-card border-0 shadow-sm overflow-hidden" >
<div class="card-header p-0 bg-white border-bottom">
<ul class="nav nav-tabs px-4 border-0 custom-console-tabs" id="consoleTabs" role="tablist">
@cantab('health and logs', 'system-monitor')
<li class="nav-item">
<button class="nav-link active py-3 px-4 small fw-bold" data-bs-toggle="tab"
data-bs-target="#tab-logs"><i class="bi bi-terminal me-2"></i>Logs</button>
</li>
@endcantab
@cantab('health and logs', 'error-logs')
<li class="nav-item">
<button class="nav-link py-3 px-4 small fw-bold" data-bs-toggle="tab"
data-bs-target="#tab-mobile"><i class="bi bi-phone me-2"></i>Mobile</button>
</li>
<li class="nav-item">
<button class="nav-link py-3 px-4 small fw-bold" data-bs-toggle="tab"
data-bs-target="#tab-sap"><i class="bi bi-broadcast me-2"></i>SAP RFC</button>
</li>
<li class="nav-item">
<button class="nav-link py-3 px-4 small fw-bold" data-bs-toggle="tab"
data-bs-target="#tab-jobs"><i class="bi bi-cpu me-2"></i>Queues</button>
</li>
@endcantab
@cantab('health and logs', 'ai-log-analysis')
<li class="nav-item">
<button class="nav-link py-3 px-4 small fw-bold text-danger" data-bs-toggle="tab"
data-bs-target="#tab-security"><i class="bi bi-shield-lock me-2"></i>Security AI</button>
</li>
@endcantab
@cantab('health and logs', 'query-logs')
<li class="nav-item">
<button class="nav-link py-3 px-4 small fw-bold" data-bs-toggle="tab"
data-bs-target="#tab-db"><i class="bi bi-search me-2"></i>Forensics</button>
</li>
@endcantab
@cantab('health and logs', 'system-monitor')
<li class="nav-item ms-auto">
<button class="nav-link py-3 px-4 small fw-bold" data-bs-toggle="tab"
data-bs-target="#tab-docs"><i class="bi bi-book me-2"></i>Documentation</button>
</li>
@endcantab
</ul>
</div>
<div class="card-body p-0">
<div class="tab-content">
<!-- LOGS -->
@cantab('health and logs', 'system-monitor')
<div class="tab-pane fade show active" id="tab-logs">
<div
class="action-bar p-3 bg-light border-bottom d-flex justify-content-between align-items-center px-4">
<span class="small fw-bold text-secondary text-uppercase ls-1">Runtime
Activity</span>
<div class="d-flex gap-2">
@can('manage health and logs')
<a href="{{ route('system-monitoring.logs.download') }}"
class="btn btn-sm btn-outline-dark rounded-pill px-3"><i
class="bi bi-file-earmark-x me-1"></i>Export CSV</a>
<button id="clear-logs-btn"
class="btn btn-sm btn-outline-danger rounded-pill px-3"><i
class="bi bi-trash me-1"></i>Clear All Logs</button>
@endcan
</div>
</div>
<div class="table-responsive">
<table id="logs-datatable"
class="table table-hover align-middle mb-0 w-100 small compact-table">
<thead>
<tr class="bg-white">
<th class="ps-4">INCIDENT TIME</th>
<th>LVL</th>
<th>MANIFEST</th>
<th class="text-end pe-4">INTEL</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
@endcantab
<!-- JOBS -->
@cantab('health and logs', 'error-logs')
<div class="tab-pane fade" id="tab-jobs">
<div
class="action-bar p-3 bg-light border-bottom d-flex justify-content-between align-items-center px-4">
<span class="small fw-bold text-secondary text-uppercase ls-1">Queue Pipeline</span>
<div class="d-flex gap-2">
@can('manage health and logs')
<button id="retry-all-jobs-btn"
class="btn btn-sm btn-outline-dark rounded-pill px-3"><i
class="bi bi-arrow-repeat me-1"></i>Retry All Jobs</button>
<button id="clear-failed-jobs-btn"
class="btn btn-sm btn-outline-danger rounded-pill px-3"><i
class="bi bi-trash me-1"></i>Flush All Jobs</button>
@endcan
</div>
</div>
<div class="table-responsive">
<table id="background-jobs-datatable"
class="table table-hover align-middle mb-0 w-100 small compact-table">
<thead>
<tr class="bg-white">
<th class="ps-4">TIMESTAMP</th>
<th>DISPOSITION</th>
<th>CHANNEL</th>
<th>DATA</th>
<th>TASK DETAIL</th>
@can('manage health and logs')
<th class="text-end pe-4">ACTION</th>
@endcan
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
<!-- SAP RFC -->
<div class="tab-pane fade" id="tab-sap">
<div class="action-bar p-3 bg-light border-bottom d-flex justify-content-between align-items-center px-4">
<span class="small fw-bold text-secondary text-uppercase ls-1">SAP RFC Trace (dev_rfc.trc)</span>
<div class="d-flex gap-2">
@can('manage health and logs')
<a href="{{ route('system-monitoring.sap-logs.download') }}" class="btn btn-sm btn-outline-dark rounded-pill px-3"><i class="bi bi-file-earmark-x me-1"></i>Export CSV</a>
<button id="clear-sap-logs-btn" class="btn btn-sm btn-outline-danger rounded-pill px-3"><i class="bi bi-trash me-1"></i>Clear All Logs</button>
@endcan
</div>
</div>
<div class="table-responsive">
<table id="sap-logs-datatable" class="table table-hover align-middle mb-0 w-100 small compact-table">
<thead>
<tr class="bg-white">
<th class="ps-4">TIMESTAMP</th>
<th>TRACE BLOCK SUMMARY</th>
<th class="text-end pe-4">DETAIL</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
<!-- MOBILE -->
<div class="tab-pane fade" id="tab-mobile">
<div class="action-bar p-3 bg-light border-bottom d-flex justify-content-between align-items-center px-4">
<span class="small fw-bold text-secondary text-uppercase ls-1">Mobile Device Telemetry</span>
<div class="d-flex gap-2">
@can('manage health and logs')
<a href="{{ route('system-monitoring.mobile-logs.download') }}" class="btn btn-sm btn-outline-dark rounded-pill px-3"><i class="bi bi-file-earmark-x me-1"></i>Export CSV</a>
<button id="clear-mobile-logs-btn" class="btn btn-sm btn-outline-danger rounded-pill px-3"><i class="bi bi-trash me-1"></i>Clear All Logs</button>
@endcan
</div>
</div>
<div class="table-responsive">
<table id="mobile-logs-datatable" class="table table-hover align-middle mb-0 w-100 small compact-table">
<thead>
<tr class="bg-white">
<th class="ps-4">TIMESTAMP</th>
<th>LVL</th>
<th>USER</th>
<th>MESSAGE</th>
<th class="text-end pe-4">DETAIL</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
@endcantab
@cantab('health and logs', 'ai-log-analysis')
<!-- SECURITY AI -->
<div class="tab-pane fade" id="tab-security">
<div class="action-bar p-3 bg-light border-bottom d-flex justify-content-between align-items-center px-4">
<span class="small fw-bold text-danger text-uppercase ls-1">AI-Driven System Hardening</span>
<button id="refresh-security-audit" class="btn btn-sm btn-outline-danger rounded-pill px-3">
<i class="bi bi-shield-check me-1"></i>RE-AUDIT SYSTEM
</button>
</div>
<div class="p-4">
<div class="row">
<div class="col-lg-3 text-center mb-4 mb-lg-0 border-end">
<div class="security-score-gauge mx-auto mb-3">
<div class="score-inner">
<h1 class="display-3 fw-black mb-0" id="ai-security-score">--</h1>
<div class="extra-small fw-bold text-uppercase opacity-50">Score</div>
</div>
</div>
<div id="security-score-label" class="badge rounded-pill px-3 py-2">SCANNING...</div>
<p class="extra-small text-muted mt-3">Last audit: <span id="ai-security-time">Never</span></p>
</div>
<div class="col-lg-9 px-lg-4">
<h6 class="fw-bold mb-3 d-flex align-items-center">
<i class="bi bi-clipboard2-pulse text-danger me-2"></i>
Security Analysis & Hardening Recommendations
</h6>
<div id="ai-security-report" class="markdown-body p-3 bg-white border rounded-4 scroll-custom" style="max-height: 500px; overflow-y: auto;">
<div class="text-center py-5 opacity-50">
<i class="bi bi-shield-lock display-4 d-block mb-3"></i>
<p class="fw-bold mb-1">No Active Audit Found</p>
<p class="small">Click the "RE-AUDIT SYSTEM" button above to initiate an AI-driven security analysis.</p>
</div>
</div>
</div>
</div>
</div>
</div>
@endcantab
<!-- DB -->
@cantab('health and logs', 'query-logs')
<div class="tab-pane fade p-4" id="tab-db">
<div class="row g-4">
<div class="col-md-6 mb-3">
<h6 class="fw-bold mb-3 small text-uppercase ls-1">Storage Metric</h6>
<div class="metric-card mb-2 d-flex justify-content-between align-items-center">
<span>Volume Footprint</span>
<span class="h4 fw-black mb-0 text-theme-1"
id="stat-db-size">{{ $stats['db_stats']['size'] }}</span>
</div>
<div class="metric-card d-flex justify-content-between align-items-center">
<span>Active Ecosystem Tables</span>
<span
class="h4 fw-black mb-0 text-dark">{{ $stats['db_stats']['tables'] }}</span>
</div>
</div>
<div class="col-md-6">
<h6 class="fw-bold mb-3 small text-uppercase ls-1">Forensic Usage Breakdown</h6>
<div id="top-tables-list" class="scroll-custom"
style="max-height: 200px; overflow-y: auto;">
@foreach($stats['db_stats']['top_tables'] ?? [] as $t)
<div
class="d-flex justify-content-between mb-2 extra-small border-bottom pb-2">
<span class="fw-bold text-dark">{{ $t->table }}</span>
<span class="fw-black text-theme-1">{{ $t->size_pretty }}</span>
</div>
@endforeach
</div>
</div>
</div>
</div>
@endcantab
{{-- DOCUMENTATION --}}
@cantab('health and logs', 'system-monitor')
<div class="tab-pane fade" id="tab-docs">
<div class="action-bar p-3 bg-light border-bottom d-flex justify-content-between align-items-center px-4">
<span class="small fw-bold text-secondary text-uppercase ls-1">Monitoring Center · Reference</span>
<span class="extra-small text-muted">Auto-refresh every 5s · Host {{ $stats['hostname'] }}</span>
</div>
<div class="p-4 p-lg-5">
{{-- HERO INTRO --}}
<div class="row align-items-center mb-5 g-4">
<div class="col-lg-8">
<h3 class="fw-black tracking-tight mb-2">What the Monitoring Center shows you</h3>
<p class="text-muted mb-0" style="font-size:.92rem;line-height:1.7;">
A single pane of glass that aggregates <span class="fw-bold text-dark">five live telemetry streams</span>
application logs, mobile clients, SAP RFC traces, queue workers, and an AI-driven security audit
on top of host vitals (CPU / RAM / Disk). All data is polled every
<span class="fw-bold text-dark">5 seconds</span>; counters animate when they change.
</p>
</div>
<div class="col-lg-4">
<div class="p-4 rounded-4 text-white" style="background:#0f172a;">
<div class="extra-small opacity-75 fw-bold mb-1">DATA SOURCES</div>
<div class="fw-bold mb-3" style="font-size:.95rem;">Where the numbers come from</div>
<div class="d-flex flex-column gap-2 extra-small">
<div><i class="bi bi-cpu me-2"></i>OS sysinfo (CPU / RAM / Disk)</div>
<div><i class="bi bi-database me-2"></i>Postgres / Redis live ping</div>
<div><i class="bi bi-file-earmark-text me-2"></i><code>storage/logs/laravel.log</code></div>
<div><i class="bi bi-broadcast me-2"></i><code>dev_rfc.trc</code> (SAP)</div>
<div><i class="bi bi-phone me-2"></i><code>mobile_logs</code> table</div>
<div><i class="bi bi-list-task me-2"></i><code>jobs</code> + <code>failed_jobs</code></div>
</div>
</div>
</div>
</div>
{{-- TOP GRID GUIDE --}}
<h6 class="fw-bold text-uppercase mb-3 ls-1">Top grid · Three columns explained</h6>
<div class="row g-3 mb-5">
<div class="col-lg-4">
<div class="p-3 rounded-4 h-100" style="background:#f8fafc;border:1px solid #e2e8f0;">
<div class="d-flex align-items-center gap-2 mb-2">
<div class="icon-box active" style="width:36px;height:36px;"><i class="bi bi-diagram-3"></i></div>
<div class="fw-bold text-dark">Infrastructure Feed (left)</div>
</div>
<div class="extra-small text-muted" style="line-height:1.6;">
Live health pings of every external service: Redis, Postgres, Reverb WebSocket, SAP RFC bridge, Mobile telemetry, Job throughput. Status text turns red as soon as a ping fails.
</div>
</div>
</div>
<div class="col-lg-4">
<div class="p-3 rounded-4 h-100" style="background:#f8fafc;border:1px solid #e2e8f0;">
<div class="d-flex align-items-center gap-2 mb-2">
<div class="icon-box" style="background:#dbeafe;color:#1d4ed8;width:36px;height:36px;"><i class="bi bi-speedometer2"></i></div>
<div class="fw-bold text-dark">Vital Numbers (middle)</div>
</div>
<div class="extra-small text-muted" style="line-height:1.6;">
Host metrics: CPU load, RAM usage with swap, Disk usage, Active Users. The percentages animate via <code>counter-value</code> and the mini-progress bar reflects load in real time.
</div>
</div>
</div>
<div class="col-lg-4">
<div class="p-3 rounded-4 h-100" style="background:#f8fafc;border:1px solid #e2e8f0;">
<div class="d-flex align-items-center gap-2 mb-2">
<div class="icon-box" style="background:#fef3c7;color:#a16207;width:36px;height:36px;"><i class="bi bi-list-task"></i></div>
<div class="fw-bold text-dark">Automation (right)</div>
</div>
<div class="extra-small text-muted" style="line-height:1.6;">
Two stacked critical counters: pending jobs and failed/incident count. These come from Laravel's <code>jobs</code> and <code>failed_jobs</code> tables and update every cycle.
</div>
</div>
</div>
</div>
{{-- TAB-BY-TAB GUIDE --}}
<h6 class="fw-bold text-uppercase mb-3 ls-1">Console tabs · What each one does</h6>
@php
$tabs = [
['icon'=>'bi-terminal', 'name'=>'Logs', 'color'=>'#0f172a', 'desc'=>'Tails <code>storage/logs/laravel.log</code>. Each row classifies INFO / WARN / ERROR / DEBUG. Click the row to open the terminal modal with the full message + stack trace. Admins can clear or export CSV.'],
['icon'=>'bi-phone', 'name'=>'Mobile', 'color'=>'#3b82f6', 'desc'=>'Telemetry from mobile clients hitting the API. Shows level, user, payload. Useful to verify a mobile deploy is reaching the right user pool.'],
['icon'=>'bi-broadcast', 'name'=>'SAP RFC', 'color'=>'#8b5cf6', 'desc'=>'Parsed blocks from <code>dev_rfc.trc</code>. Each block represents a roundtrip to SAP. Critical for diagnosing integration failures between Laravel and the SAP backend.'],
['icon'=>'bi-cpu', 'name'=>'Queues', 'color'=>'#f59e0b', 'desc'=>'Lists pending and failed background jobs. From here you can retry any failed job individually, retry all at once, or flush the queue entirely.'],
['icon'=>'bi-shield-lock', 'name'=>'Security AI', 'color'=>'#ef4444', 'desc'=>'Runs an AI-driven security audit of the running app: env file exposure, weak headers, missing CSRF / rate-limits, debug mode in prod, default credentials. Returns a numerical score + markdown hardening report.'],
['icon'=>'bi-search', 'name'=>'Forensics', 'color'=>'#22c55e', 'desc'=>'Database volume footprint, total table count, and a ranked list of the heaviest tables. Use this when storage growth becomes a concern.'],
];
@endphp
<div class="row g-3 mb-5">
@foreach($tabs as $t)
<div class="col-md-6 col-lg-4">
<div class="p-3 rounded-4 h-100" style="background:#fff;border:1px solid #e2e8f0;">
<div class="d-flex align-items-center gap-2 mb-2">
<div class="icon-box" style="background:{{ $t['color'] }}1a;color:{{ $t['color'] }};width:36px;height:36px;">
<i class="{{ $t['icon'] }}"></i>
</div>
<div class="fw-bold text-dark">{{ $t['name'] }}</div>
</div>
<div class="extra-small text-muted" style="line-height:1.6;">{!! $t['desc'] !!}</div>
</div>
</div>
@endforeach
</div>
{{-- SIGNAL DICTIONARY --}}
<h6 class="fw-bold text-uppercase mb-3 ls-1">
<i class="bi bi-flag-fill text-warning me-1"></i>How to read the indicators
</h6>
<div class="row g-2 mb-5">
<div class="col-md-6">
<div class="p-3 rounded-4 h-100" style="background:#f0fdf4;border:1px solid #bbf7d0;">
<div class="d-flex align-items-center gap-2 mb-1">
<span class="pulse-badge success">ONLINE / CONNECTED / RUNNING</span>
</div>
<div class="extra-small text-muted">All systems responding within latency budget. The pulsing dot animation indicates a recent successful ping.</div>
</div>
</div>
<div class="col-md-6">
<div class="p-3 rounded-4 h-100" style="background:#fef2f2;border:1px solid #fecaca;">
<div class="d-flex align-items-center gap-2 mb-1">
<span class="extra-small fw-bold text-danger">DISCONNECTED / OFFLINE</span>
</div>
<div class="extra-small text-muted">Service is unreachable. Check the corresponding env config (Redis host, DB credentials, SAP destination) and that the underlying daemon is running.</div>
</div>
</div>
<div class="col-md-6">
<div class="p-3 rounded-4 h-100" style="background:#fffbeb;border:1px solid #fde68a;">
<div class="d-flex align-items-center gap-2 mb-1">
<span class="extra-small fw-bold" style="color:#a16207;">IDLE</span>
</div>
<div class="extra-small text-muted">Service is configured but not currently emitting traffic not necessarily broken (e.g. Reverb with no active subscribers).</div>
</div>
</div>
<div class="col-md-6">
<div class="p-3 rounded-4 h-100" style="background:#eff6ff;border:1px solid #bfdbfe;">
<div class="d-flex align-items-center gap-2 mb-1">
<span class="extra-small fw-bold text-primary">STABLE</span>
</div>
<div class="extra-small text-muted">Database engine status Postgres responding, latency under threshold.</div>
</div>
</div>
</div>
{{-- THRESHOLDS --}}
<h6 class="fw-bold text-uppercase mb-3 ls-1">
<i class="bi bi-thermometer-half text-info me-1"></i>Recommended thresholds
</h6>
<div class="table-responsive mb-5">
<table class="table table-hover align-middle small mb-0" style="background:#f8fafc;border-radius:12px;overflow:hidden;">
<thead style="background:#e2e8f0;">
<tr>
<th class="ps-3">METRIC</th>
<th>HEALTHY</th>
<th>WARN</th>
<th>CRITICAL</th>
<th>WHAT TO DO</th>
</tr>
</thead>
<tbody>
<tr>
<td class="ps-3 fw-bold text-dark">CPU Load</td>
<td><span class="pulse-badge success">&lt; 60%</span></td>
<td><span class="extra-small text-warning fw-bold">6080%</span></td>
<td><span class="extra-small text-danger fw-bold">&gt; 80%</span></td>
<td class="text-muted">Check long-running queries / N+1 patterns / runaway workers.</td>
</tr>
<tr>
<td class="ps-3 fw-bold text-dark">RAM Usage</td>
<td><span class="pulse-badge success">&lt; 70%</span></td>
<td><span class="extra-small text-warning fw-bold">7085%</span></td>
<td><span class="extra-small text-danger fw-bold">&gt; 85% + swap</span></td>
<td class="text-muted">Restart PHP-FPM / inspect cache size / scale instance.</td>
</tr>
<tr>
<td class="ps-3 fw-bold text-dark">Disk Usage</td>
<td><span class="pulse-badge success">&lt; 75%</span></td>
<td><span class="extra-small text-warning fw-bold">7590%</span></td>
<td><span class="extra-small text-danger fw-bold">&gt; 90%</span></td>
<td class="text-muted">Rotate logs, prune old backups, run Forensics tab to find heavy tables.</td>
</tr>
<tr>
<td class="ps-3 fw-bold text-dark">Failed Jobs</td>
<td><span class="pulse-badge success">0</span></td>
<td><span class="extra-small text-warning fw-bold">110</span></td>
<td><span class="extra-small text-danger fw-bold">&gt; 10 or rising</span></td>
<td class="text-muted">Open Queues tab, inspect payload, retry or flush.</td>
</tr>
<tr>
<td class="ps-3 fw-bold text-dark">Security Score</td>
<td><span class="pulse-badge success"> 85</span></td>
<td><span class="extra-small text-warning fw-bold">6084</span></td>
<td><span class="extra-small text-danger fw-bold">&lt; 60</span></td>
<td class="text-muted">Re-audit in Security AI tab, apply recommendations.</td>
</tr>
</tbody>
</table>
</div>
{{-- SECURITY AI HOW IT WORKS --}}
<h6 class="fw-bold text-uppercase mb-3 ls-1">
<i class="bi bi-shield-fill-check text-danger me-1"></i>Security AI · How the audit works
</h6>
<div class="row g-3 mb-5">
<div class="col-md-6">
<div class="p-3 rounded-4 h-100" style="background:#fff;border:1px solid #e2e8f0;">
<div class="fw-bold text-dark mb-2">What it inspects</div>
<ul class="extra-small text-muted mb-0" style="line-height:1.8;padding-left:1.2rem;">
<li>Debug mode flag in production</li>
<li>HTTPS / HSTS / security headers</li>
<li>Default / weak credentials in seeders</li>
<li>Rate-limiting on auth endpoints</li>
<li>CSRF token enforcement</li>
<li>Session driver &amp; cookie flags</li>
<li>File permissions on <code>.env</code>, <code>storage/</code></li>
</ul>
</div>
</div>
<div class="col-md-6">
<div class="p-3 rounded-4 h-100" style="background:#fff;border:1px solid #e2e8f0;">
<div class="fw-bold text-dark mb-2">How to use</div>
<ol class="extra-small text-muted mb-0" style="line-height:1.8;padding-left:1.2rem;">
<li>Click <span class="fw-bold text-danger">RE-AUDIT SYSTEM</span></li>
<li>AI scans config + runtime state</li>
<li>Returns a score 0100 and a Markdown report</li>
<li>Apply each "Recommendation" row by row typically copy/paste config changes</li>
<li>Re-audit to confirm the score improved</li>
</ol>
</div>
</div>
</div>
{{-- PERMISSIONS --}}
<h6 class="fw-bold text-uppercase mb-3 ls-1">
<i class="bi bi-person-badge text-secondary me-1"></i>Permissions
</h6>
<div class="row g-2 mb-5">
<div class="col-md-4">
<div class="p-3 rounded-4 h-100" style="background:#f0fdf4;border:1px solid #bbf7d0;">
<div class="fw-bold text-success mb-1"><code class="text-success">view health and logs</code></div>
<div class="extra-small text-muted">Required to <span class="fw-bold">open</span> the Monitoring Center and view standard telemetry (Logs, Mobile, SAP, Queues).</div>
</div>
</div>
<div class="col-md-4">
<div class="p-3 rounded-4 h-100" style="background:#fef2f2;border:1px solid #fecaca;">
<div class="fw-bold text-danger mb-1"><code class="text-danger">manage health and logs</code></div>
<div class="extra-small text-muted">Required to <span class="fw-bold">clear logs</span>, <span class="fw-bold">retry / flush jobs</span>, and <span class="fw-bold">toggle maintenance</span>.</div>
</div>
</div>
<div class="col-md-4">
<div class="p-3 rounded-4 h-100" style="background:#fff7ed;border:1px solid #ffedd5;">
<div class="fw-bold text-warning mb-1"><code class="text-warning">view ai log analysis</code></div>
<div class="extra-small text-muted">Exclusive to <span class="fw-bold text-dark">Developer</span> role. Required to access the <span class="fw-bold">Security AI Audit</span> tab and run AI-driven scans.</div>
</div>
</div>
</div>
{{-- HEALTH CHECKLIST (LIVE) --}}
<h6 class="fw-bold text-uppercase mb-3 ls-1">
<i class="bi bi-activity text-success me-1"></i>Live health snapshot
</h6>
<div class="p-3 rounded-4 mb-5" style="background:#0c121e;color:#e2e8f0;border:1px solid #1e293b;">
<div class="extra-small fw-bold text-success mb-2" style="letter-spacing:1px;font-family:'Fira Code',monospace;">$ monitor --self-check</div>
<pre class="mb-0" style="font-family:'Fira Code',monospace;font-size:.78rem;line-height:1.8;color:#cbd5e1;white-space:pre-wrap;">
<span style="color:#fbbf24;">[01]</span> Host: <span style="color:#22c55e;">{{ $stats['hostname'] }}</span>
<span style="color:#fbbf24;">[02]</span> Uptime: <span style="color:#22c55e;">{{ $stats['uptime'] }}</span>
<span style="color:#fbbf24;">[03]</span> CPU load: <span style="color:{{ $stats['cpu'] > 80 ? '#ef4444' : ($stats['cpu'] > 60 ? '#f59e0b' : '#22c55e') }};">{{ $stats['cpu'] }}%</span>
<span style="color:#fbbf24;">[04]</span> RAM usage: <span style="color:{{ $stats['ram']['percentage'] > 85 ? '#ef4444' : ($stats['ram']['percentage'] > 70 ? '#f59e0b' : '#22c55e') }};">{{ $stats['ram']['percentage'] }}%</span> ({{ $stats['ram']['used'] }})
<span style="color:#fbbf24;">[05]</span> Disk usage: <span style="color:{{ $stats['disk']['percentage'] > 90 ? '#ef4444' : ($stats['disk']['percentage'] > 75 ? '#f59e0b' : '#22c55e') }};">{{ $stats['disk']['percentage'] }}%</span> ({{ $stats['disk']['total'] }} available)
<span style="color:#fbbf24;">[06]</span> Redis: <span style="color:{{ ($stats['redis']['status'] ?? '') === 'connected' ? '#22c55e' : '#ef4444' }};">{{ ($stats['redis']['status'] ?? 'unknown') }}</span>
<span style="color:#fbbf24;">[07]</span> Reverb WS: <span style="color:{{ $stats['has_reverb'] ? '#22c55e' : '#94a3b8' }};">{{ $stats['has_reverb'] ? 'active' : 'idle' }}</span>
<span style="color:#fbbf24;">[08]</span> SAP RFC: <span style="color:{{ $stats['sap']['active'] ? '#22c55e' : '#94a3b8' }};">{{ $stats['sap']['status'] }}</span>
<span style="color:#fbbf24;">[09]</span> DB tables: <span style="color:#22c55e;">{{ $stats['db_stats']['tables'] }}</span>
<span style="color:#fbbf24;">[10]</span> Pending: <span style="color:{{ ($stats['queues']['pending'] ?? 0) > 0 ? '#f59e0b' : '#22c55e' }};">{{ $stats['queues']['pending'] ?? 0 }}</span> jobs
<span style="color:#fbbf24;">[11]</span> Failed: <span style="color:{{ ($stats['queues']['failed'] ?? 0) > 0 ? '#ef4444' : '#22c55e' }};">{{ $stats['queues']['failed'] ?? 0 }}</span> jobs
<span style="color:#94a3b8;"># This snapshot was rendered at page load. The dashboard above
# refreshes every 5 seconds via /api/system-stats.</span></pre>
</div>
{{-- TROUBLESHOOTING --}}
<h6 class="fw-bold text-uppercase mb-3 ls-1">
<i class="bi bi-wrench-adjustable-circle text-warning me-1"></i>Common scenarios &amp; remedies
</h6>
<div class="table-responsive mb-5">
<table class="table table-hover align-middle small mb-0" style="background:#f8fafc;border-radius:12px;overflow:hidden;">
<thead style="background:#e2e8f0;">
<tr>
<th class="ps-3">SYMPTOM</th>
<th>FIRST CHECK</th>
<th>LIKELY FIX</th>
</tr>
</thead>
<tbody>
<tr>
<td class="ps-3 fw-bold text-dark">Logs tab is empty</td>
<td class="text-muted">File permissions on <code>storage/logs/laravel.log</code></td>
<td class="text-muted">Run <code>chmod -R 775 storage/</code></td>
</tr>
<tr>
<td class="ps-3 fw-bold text-dark">Redis shows DISCONNECTED</td>
<td class="text-muted"><code>REDIS_HOST</code> / port in <code>.env</code></td>
<td class="text-muted">Restart redis container / verify <code>redis-cli ping</code></td>
</tr>
<tr>
<td class="ps-3 fw-bold text-dark">Many failed jobs</td>
<td class="text-muted">Open Queues tab, inspect any row</td>
<td class="text-muted">Retry one to surface the error, fix root cause, then Retry All</td>
</tr>
<tr>
<td class="ps-3 fw-bold text-dark">SAP RFC blank</td>
<td class="text-muted">Trace file <code>dev_rfc.trc</code> exists &amp; readable</td>
<td class="text-muted">Restart SAP RFC bridge / enable trace level in SAP config</td>
</tr>
<tr>
<td class="ps-3 fw-bold text-dark">Security score dropped</td>
<td class="text-muted">Read the latest report carefully</td>
<td class="text-muted">Apply each recommendation, re-audit until back to green</td>
</tr>
<tr>
<td class="ps-3 fw-bold text-dark">Disk &gt; 90%</td>
<td class="text-muted">Forensics tab heaviest tables</td>
<td class="text-muted">Truncate logs / archive old data / increase volume</td>
</tr>
</tbody>
</table>
</div>
{{-- FOOTER NOTE --}}
<div class="d-flex align-items-center gap-3 p-3 rounded-4" style="background:#eff6ff;border:1px solid #bfdbfe;">
<i class="bi bi-info-circle-fill text-primary fs-3"></i>
<div>
<div class="fw-bold text-dark mb-1" style="font-size:.85rem;">Performance notes</div>
<div class="extra-small text-muted">
Polling runs at <code>5000ms</code>. To reduce server load on slower environments, increase <code>REFRESH_RATE</code> in this view's script.
The Security AI audit is on-demand only (never polled) because each run consumes AI tokens. Active tab is persisted in <code>localStorage</code> as <code>systemMonitoringActiveTab</code>.
</div>
</div>
</div>
</div>
</div>
@endcantab
</div>
</div>
</div>
</div>
</div>
</div>
<!-- TERMINAL MODAL: ANIMATED -->
<div class="modal fade animate__animated animate__fadeIn" id="logDetailModal" tabindex="-1">
<div class="modal-dialog modal-xl animate__animated animate__fadeIn">
<div class="modal-content adminuiux-card border-0 shadow-2xl terminal-box">
<div class="modal-header terminal-header">
<div class="window-controls me-3">
<span class="dot red"></span><span class="dot yellow"></span><span class="dot green"></span>
</div>
<h6 class="modal-title fw-bold extra-small text-white-50 letter-spacing-1">TELEMETRY_DUMP @node_01
</h6>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body p-0">
<div class="code-container p-4">
<pre id="detail-message" class="mb-0 small text-success font-monospace scroll-custom"
style="max-height: 700px; overflow-y: auto; overflow-x: hidden; line-height: 1.6; white-space: pre-wrap; word-break: break-all;"></pre>
</div>
</div>
</div>
</div>
</div>
@push('scripts')
<!-- ADD ANIMATE.CSS CDN -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" />
<script>
$(document).ready(function () {
// High-Performance Polling
const REFRESH_RATE = 5000;
// ===== TAB PERSISTENCE =====
(function () {
const STORAGE_KEY = 'systemMonitoringActiveTab';
// Save tab whenever user switches
document.querySelectorAll('[data-bs-toggle="tab"]').forEach(function (tabEl) {
tabEl.addEventListener('shown.bs.tab', function (e) {
localStorage.setItem(STORAGE_KEY, e.target.getAttribute('data-bs-target'));
});
});
// Restore saved tab on page load
const savedTab = localStorage.getItem(STORAGE_KEY);
if (savedTab) {
const tabEl = document.querySelector(`[data-bs-target="${savedTab}"]`);
if (tabEl) {
setTimeout(() => {
bootstrap.Tab.getOrCreateInstance(tabEl).show();
}, 50);
}
}
})();
// Logs Init
const logsTable = $('#logs-datatable').DataTable({
processing: true, serverSide: true, ajax: '{{ route("system-monitoring.logs.datatable") }}',
order: [[0, 'desc']], pageLength: 10, autoWidth: false, dom: 'tr<"d-flex justify-content-between p-3 border-top"ip>',
columns: [{ data: 0, className: 'ps-4 datetime-col' }, { data: 1 }, { data: 2 }, { data: 3, className: 'pe-4 text-end', orderable: false }],
drawCallback: function () {
$('.view-log-detail').off('click').on('click', function () {
const log = $(this).data('log');
$('#detail-message').text(log.message);
new bootstrap.Modal('#logDetailModal').show();
});
}
});
// Jobs Init
const jobsTable = $('#background-jobs-datatable').DataTable({
processing: true, serverSide: true, ajax: '{{ route("system-monitoring.background-jobs.datatable") }}',
order: [[0, 'desc']], pageLength: 10, autoWidth: false, dom: 'tr<"d-flex justify-content-between p-3 border-top"ip>',
columns: [
{ data: 0, className: 'ps-4' },
{ data: 1 },
{ data: 2 },
{ data: 3 },
{ data: 4 }
@can('manage health and logs')
, { data: 5, className: 'pe-4 text-end', orderable: false }
@endcan
],
drawCallback: function () {
$('.retry-job-btn, .delete-failed-job-btn, .view-job-detail').off('click').on('click', function () {
const id = $(this).data('id');
if ($(this).hasClass('view-job-detail')) {
const j = $(this).data('job'); $('#detail-message').text(j.exception + "\n\n" + JSON.stringify(JSON.parse(j.payload), null, 2));
new bootstrap.Modal('#logDetailModal').show();
} else {
const url = $(this).hasClass('retry-job-btn') ? '{{ route("system-monitoring.background-jobs.retry", ["id" => ":id"]) }}' : '{{ route("system-monitoring.background-jobs.delete-failed", ["id" => ":id"]) }}';
$.post(url.replace(':id', id), { _token: '{{ csrf_token() }}' }, () => { jobsTable.ajax.reload(null, false); refreshStats(); });
}
});
}
});
function refreshStats() {
const btn = $('#refresh-all-stats');
btn.find('i').addClass('bi-spin');
$.get('{{ route("system-monitoring.stats") }}', function (d) {
// Update and Animate numbers if changed
// RAM & Swap
updateVal('#stat-ram-percent', d.ram.percentage + '%');
$('#stat-ram-used').text(d.ram.used + ' used');
if (d.ram.swap) {
$('#stat-swap-info').text('Swap: ' + d.ram.swap.percentage + '%');
}
updateVal('#stat-disk-percent', d.disk.percentage + '%');
// Refined Users
updateVal('#stat-users-count', d.users.total);
$('#stat-users-auth').text(d.users.authenticated);
// Dynamic DB
$('#stat-db-meta').text(d.db_stats.tables + ' Tables | ' + d.db_stats.latency);
$('#stat-db-status').text(d.db_stats.status)
.removeClass('text-success text-danger text-warning')
.addClass(d.db_stats.status === 'STABLE' ? 'text-success' : (d.db_stats.status === 'OFFLINE' ? 'text-danger' : 'text-warning'));
updateVal('#stat-queues-pending', d.queues.pending);
updateVal('#stat-queues-failed', d.queues.failed);
$('#stat-queues-load-text').text(d.queues.load_factor + ' tasks/min');
$('#stat-uptime-badge').text(d.uptime);
$('#cpu-bar').css('width', d.cpu + '%');
// SAP & Mobile
$('#sap-icon').toggleClass('active', d.sap.active);
$('#stat-sap-detail').text('Trace: ' + d.sap.size);
$('#stat-sap-status').text(d.sap.status).toggleClass('text-success', d.sap.active).toggleClass('text-muted', !d.sap.active);
$('#stat-mobile-detail').text(d.mobile.total_logs + ' Events recorded');
$('#stat-mobile-activity').text(d.mobile.last_activity);
// Forensics Update
$('#stat-db-size').text(d.db_stats.size);
let tableHtml = '';
d.db_stats.top_tables.forEach(t => {
tableHtml += `<div class="d-flex justify-content-between mb-2 small border-bottom pb-1">
<span class="text-truncate me-2">${t.table}</span>
<span class="fw-bold text-nowrap">${t.size_pretty}</span>
</div>`;
});
$('#top-tables-list').html(tableHtml);
// Socket Feedback
if (d.has_reverb) {
$('#reverb-icon').addClass('active');
$('#reverb-status-text').text('ACTIVE').removeClass('text-muted').addClass('text-success');
} else {
$('#reverb-icon').removeClass('active');
$('#reverb-status-text').text('IDLE').addClass('text-muted').removeClass('text-success');
}
// Silent Table Update
logsTable.ajax.reload(null, false);
jobsTable.ajax.reload(null, false);
sapTable.ajax.reload(null, false);
mobileTable.ajax.reload(null, false);
setTimeout(() => btn.find('i').removeClass('bi-spin'), 1000);
});
}
// SAP Init
const sapTable = $('#sap-logs-datatable').DataTable({
processing: true, serverSide: true, ajax: '{{ route("system-monitoring.sap-logs.datatable") }}',
order: [[0, 'desc']], pageLength: 10, autoWidth: false, dom: 'tr<"d-flex justify-content-between p-3 border-top"ip>',
columns: [{ data: 0, className: 'ps-4 datetime-col' }, { data: 1 }, { data: 2, className: 'pe-4 text-end', orderable: false }],
drawCallback: function () {
$('.view-sap-log-detail').off('click').on('click', function () {
$('#detail-message').text($(this).data('message'));
new bootstrap.Modal('#logDetailModal').show();
});
}
});
// Mobile Init
const mobileTable = $('#mobile-logs-datatable').DataTable({
processing: true, serverSide: true, ajax: '{{ route("system-monitoring.mobile-logs.datatable") }}',
order: [[0, 'desc']], pageLength: 10, autoWidth: false, dom: 'tr<"d-flex justify-content-between p-3 border-top"ip>',
columns: [{ data: 0, className: 'ps-4 datetime-col' }, { data: 1 }, { data: 2 }, { data: 3 }, { data: 4, className: 'pe-4 text-end', orderable: false }],
drawCallback: function () {
$('.view-log-detail').off('click').on('click', function () {
const log = $(this).data('log');
$('#detail-message').text(log.message + (log.context ? "\n\nContext: " + JSON.stringify(log.context, null, 2) : ""));
new bootstrap.Modal('#logDetailModal').show();
});
}
});
function updateVal(selector, newVal) {
const el = $(selector);
if (el.text() !== newVal.toString()) {
el.text(newVal).addClass('animate__animated animate__pulse');
setTimeout(() => el.removeClass('animate__animated animate__pulse'), 1000);
}
}
$('#refresh-all-stats').on('click', refreshStats);
$('#retry-all-jobs-btn, #clear-failed-jobs-btn, #clear-logs-btn, #clear-sap-logs-btn, #clear-mobile-logs-btn').on('click', function () {
let url;
switch(this.id) {
case 'clear-logs-btn': url = '{{ route("system-monitoring.logs.clear") }}'; break;
case 'clear-sap-logs-btn': url = '{{ route("system-monitoring.sap-logs.clear") }}'; break;
case 'clear-mobile-logs-btn': url = '{{ route("system-monitoring.mobile-logs.clear") }}'; break;
case 'clear-failed-jobs-btn': url = '{{ route("system-monitoring.background-jobs.clear") }}'; break;
case 'retry-all-jobs-btn': url = '{{ route("system-monitoring.background-jobs.retry") }}'; break;
}
StandardSwal.fire({ title: 'Confirmation', text: 'Apply operation?', icon: 'info', showCancelButton: true }).then(r => { if (r.isConfirmed) $.post(url, { _token: '{{ csrf_token() }}' }, () => { refreshStats(); }); });
});
function runSecurityAudit(refresh = false) {
const report = $('#ai-security-report');
const score = $('#ai-security-score');
const label = $('#security-score-label');
const time = $('#ai-security-time');
if (refresh) {
report.html('<div class="placeholder-glow"><span class="placeholder col-7"></span><span class="placeholder col-4"></span><span class="placeholder col-4"></span><span class="placeholder col-6"></span></div>');
score.text('--');
}
$.get('{{ route("ai.security-audit") }}', { refresh: refresh ? 1 : 0 }, function(d) {
if (d.error) {
report.html('<div class="alert alert-danger">'+d.error+'</div>');
return;
}
// Parse Score
const s = d.score;
score.text(s);
if (s >= 90) { label.text('EXCELLENT').addClass('text-bg-success').removeClass('text-bg-warning text-bg-danger'); }
else if (s >= 70) { label.text('MODERATE').addClass('text-bg-warning').removeClass('text-bg-success text-bg-danger'); }
else { label.text('CRITICAL').addClass('text-bg-danger').removeClass('text-bg-success text-bg-warning'); }
time.text(d.timestamp);
// Render Markdown (using marked if available, else simple replace)
if (window.marked) {
report.html(marked.parse(d.analysis));
} else {
report.html(d.analysis.replace(/\n/g, '<br>'));
}
// Transform Risk Levels into Badges
report.find('td').each(function() {
const text = $(this).text().trim().toUpperCase();
if (text === 'CRITICAL') $(this).html('<span class="badge rounded-pill text-bg-danger px-3 py-1">CRITICAL</span>');
else if (text === 'HIGH') $(this).html('<span class="badge rounded-pill text-bg-warning px-3 py-1 text-dark">HIGH</span>');
else if (text === 'MEDIUM') $(this).html('<span class="badge rounded-pill text-bg-info px-3 py-1 text-dark">MEDIUM</span>');
else if (text === 'LOW') $(this).html('<span class="badge rounded-pill text-bg-success px-3 py-1">LOW</span>');
});
});
}
// Manual trigger only (removed auto-trigger on tab show)
$('#refresh-security-audit').on('click', function() {
$(this).find('i').addClass('bi-spin');
runSecurityAudit(true);
setTimeout(() => $(this).find('i').removeClass('bi-spin'), 1500);
});
setInterval(refreshStats, REFRESH_RATE);
});
function copyLogDetail() {
const el = document.getElementById('detail-message');
const range = document.createRange(); range.selectNode(el);
window.getSelection().removeAllRanges(); window.getSelection().addRange(range);
document.execCommand('copy'); window.getSelection().removeAllRanges();
toastr?.success('Content copied to clipboard');
}
</script>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js" crossorigin="anonymous"></script>
<style>
/* THE COOL FACTOR CSS */
.security-score-gauge {
width: 150px;
height: 150px;
border-radius: 50%;
background: conic-gradient(var(--adminuiux-theme-1) 0%, #f1f5f9 0%);
display: flex;
align-items: center;
justify-content: center;
position: relative;
border: 10px solid #f8fafc;
box-shadow: inset 0 0 15px rgba(0,0,0,0.05);
}
.score-inner {
width: 110px;
height: 110px;
background: white;
border-radius: 50%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
box-shadow: 0 10px 20px rgba(0,0,0,0.05);
}
.fw-black {
font-weight: 900;
}
.ls-1 {
letter-spacing: 1px;
}
.tracking-tight {
letter-spacing: -2px;
}
/* Typography Scale */
.display-3 {
font-size: 3.5rem;
font-weight: 900;
letter-spacing: -2px;
}
.display-1 {
font-size: 4.5rem;
fw-black;
letter-spacing: -3px;
line-height: 0.8;
}
.extra-small {
font-size: 0.85rem;
letter-spacing: 0.2px;
}
.hover-lift {
transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
.hover-lift:hover {
transform: translateY(-10px);
box-shadow: 0 15px 30px rgba(0, 0, 0, 0.1) !important;
}
.bg-dark {
background: #1a1a1a !important;
}
.bg-decoration {
position: absolute;
right: -50px;
bottom: -50px;
width: 200px;
height: 200px;
background: rgba(255, 255, 255, 0.03);
border-radius: 50%;
z-index: 0;
}
.pulse-badge {
padding: 4px 12px;
border-radius: 20px;
font-size: 0.7rem;
font-weight: 800;
display: flex;
align-items: center;
gap: 6px;
}
.pulse-badge::before {
content: '';
width: 8px;
height: 8px;
border-radius: 50%;
display: inline-block;
animation: pulse-glow 1.5s infinite;
}
.pulse-badge.success {
background: #dcfce7;
color: #15803d;
}
.pulse-badge.success::before {
background: #22c55e;
box-shadow: 0 0 8px #22c55e;
}
@keyframes pulse-glow {
0% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(1.5);
opacity: 0.5;
}
100% {
transform: scale(1);
opacity: 1;
}
}
.icon-box {
width: 40px;
height: 40px;
background: #f1f5f9;
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
color: #64748b;
transition: all 0.3s;
}
.icon-box.active {
background: #dcfce7;
color: #22c55e;
animation: float 2s infinite ease-in-out;
}
@keyframes float {
0%,
100% {
transform: translateY(0);
}
50% {
transform: translateY(-5px);
}
}
.mini-progress {
height: 4px;
background: #f1f5f9;
border-radius: 2px;
overflow: hidden;
}
.mini-progress .bar {
height: 100%;
background: var(--adminuiux-theme-1);
transition: width 1s;
}
.card-glow-warning:hover {
box-shadow: 0 0 20px rgba(245, 158, 11, 0.2) !important;
}
.card-glow-theme:hover {
box-shadow: 0 0 20px rgba(var(--adminuiux-theme-1-rgb), 0.3) !important;
}
/* Terminal Styling */
.terminal-box {
background: #0c121e !important;
border: 1px solid #1e293b !important;
color: #10b981 !important;
min-height: 500px;
display: flex;
flex-direction: column;
}
.terminal-header {
background: #1e293b !important;
border-bottom: 1px solid #334155 !important;
padding: 12px 20px !important;
display: flex;
align-items: center;
justify-content: space-between;
}
.window-controls .dot {
width: 12px;
height: 12px;
border-radius: 50%;
display: inline-block;
margin-right: 6px;
}
.dot.red {
background: #ef4444;
}
.dot.yellow {
background: #f59e0b;
}
.dot.green {
background: #22c55e;
}
.code-container pre {
font-family: 'Fira Code', 'JetBrains Mono', monospace;
}
.bi-spin {
animation: spin 1s infinite linear;
display: inline-block;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.custom-console-tabs .nav-link {
color: #94a3b8;
border-bottom-width: 3px !important;
}
.custom-console-tabs .nav-link.active {
color: var(--adminuiux-theme-1);
}
.metric-card {
padding: 15px;
background: #f8fafc;
border: 1px solid #e2e8f0;
border-radius: 12px;
}
/* Custom Scrollbar */
.scroll-custom::-webkit-scrollbar {
width: 6px;
}
.scroll-custom::-webkit-scrollbar-track {
background: #f1f5f9;
}
.scroll-custom::-webkit-scrollbar-thumb {
background: #cbd5e1;
border-radius: 10px;
}
.scroll-custom::-webkit-scrollbar-thumb:hover {
background: #94a3b8;
}
/* AI Security Report Typography */
#ai-security-report {
color: #1e293b !important;
line-height: 1.7;
}
#ai-security-report h1, #ai-security-report h2, #ai-security-report h3,
#ai-security-report h4, #ai-security-report h5, #ai-security-report h6 {
color: #0f172a !important;
font-weight: 800;
margin-top: 1.5rem;
margin-bottom: 1rem;
border-bottom: none;
}
#ai-security-report p, #ai-security-report li {
color: #334155 !important;
font-size: 0.95rem;
margin-bottom: 0.8rem;
}
#ai-security-report strong {
color: #0f172a !important;
font-weight: 700;
}
#ai-security-report code {
background: #f1f5f9;
color: #e11d48;
padding: 0.2rem 0.4rem;
border-radius: 4px;
font-size: 0.85rem;
}
/* AI Security Report Table Overrides */
#ai-security-report table {
width: 100% !important;
display: table !important;
border-collapse: separate !important;
border-spacing: 0 !important;
margin-top: 1.5rem !important;
margin-bottom: 2rem !important;
border: 1px solid #e2e8f0 !important;
border-radius: 12px !important;
overflow: hidden !important;
background-color: #ffffff !important;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05) !important;
}
#ai-security-report th {
background-color: #f8fafc !important;
color: #0f172a !important;
font-weight: 800 !important;
text-transform: uppercase !important;
font-size: 0.75rem !important;
letter-spacing: 0.1em !important;
padding: 14px 18px !important;
border-bottom: 2px solid #e2e8f0 !important;
text-align: left !important;
}
#ai-security-report td {
padding: 14px 18px !important;
color: #1e293b !important;
font-size: 0.9rem !important;
border-bottom: 1px solid #f1f5f9 !important;
background-color: #ffffff !important;
line-height: 1.5 !important;
vertical-align: top !important;
}
#ai-security-report tr:hover td {
background-color: #fcfdfe !important;
}
#ai-security-report tr:last-child td {
border-bottom: none !important;
}
/* Fix for potential dark background inheritance */
.markdown-body table, .markdown-body tr, .markdown-body td, .markdown-body th {
background-color: transparent !important;
border-color: #e2e8f0 !important;
color: inherit !important;
}
/* AI Security Report Code Block Styling */
#ai-security-report pre {
background-color: #0f172a !important;
color: #f8fafc !important;
padding: 1.25rem !important;
border-radius: 12px !important;
margin-top: 1rem !important;
margin-bottom: 1.5rem !important;
border: 1px solid #1e293b !important;
font-family: 'Fira Code', 'JetBrains Mono', 'Courier New', monospace !important;
font-size: 0.85rem !important;
line-height: 1.6 !important;
overflow-x: auto !important;
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1) !important;
position: relative !important;
}
#ai-security-report pre::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4px;
background: linear-gradient(90deg, #3b82f6, #8b5cf6, #ec4899);
border-top-left-radius: 12px;
border-top-right-radius: 12px;
opacity: 0.8;
}
#ai-security-report pre code {
background-color: transparent !important;
color: #e2e8f0 !important;
padding: 0 !important;
border-radius: 0 !important;
font-size: inherit !important;
}
/* Simple Syntax Highlighting (pseudo-classes for AI content) */
#ai-security-report .comment { color: #64748b !important; font-style: italic; }
#ai-security-report .keyword { color: #f472b6 !important; }
#ai-security-report .string { color: #34d399 !important; }
</style>
@endpush
</x-app-layout>