New Blade layout, copy, and visual system for the monitoring tab; RRD hosts use data-statistics-chart hooks; tables use pxStatTableKit and data-statistics-data-row; network and comparison panels use new selectors; CSV helpers renamed (pxExport*); modals and telemetry refreshed. Made-with: Cursor
323 lines
24 KiB
PHP
323 lines
24 KiB
PHP
<!-- Historical Data Comparison Section -->
|
|
@php
|
|
$historicalComparison = $this->historicalComparison;
|
|
@endphp
|
|
<div class="mt-10 ring-1 ring-neutral/40 rounded-2xl overflow-hidden bg-background-secondary/80 shadow-sm" data-statistics-panel="period-compare" id="px-stat-period-compare">
|
|
<div class="flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between p-4 border-b border-neutral bg-gradient-to-r from-primary/10 via-primary/5 to-transparent">
|
|
<div class="flex items-start gap-3 min-w-0 flex-1">
|
|
<div class="flex items-center justify-center w-10 h-10 rounded-lg bg-primary/15 shrink-0">
|
|
<x-ri-bar-chart-2-line class="size-5 text-primary" />
|
|
</div>
|
|
<div class="min-w-0">
|
|
<div class="flex items-center gap-2 flex-wrap">
|
|
<h3 class="text-base font-semibold">Historical Data Comparison</h3>
|
|
<div class="relative" x-data="{ open: false }" x-cloak>
|
|
<button type="button" @click="open = true" class="text-base/50 hover:text-base/70 transition-colors">
|
|
<x-ri-question-line class="size-4" />
|
|
</button>
|
|
<!-- Modal Backdrop -->
|
|
<div x-show="open"
|
|
x-transition:enter="transition ease-out duration-300"
|
|
x-transition:enter-start="opacity-0"
|
|
x-transition:enter-end="opacity-100"
|
|
x-transition:leave="transition ease-in duration-200"
|
|
x-transition:leave-start="opacity-100"
|
|
x-transition:leave-end="opacity-0"
|
|
@click="open = false"
|
|
@keydown.escape.window="open = false"
|
|
class="fixed inset-0 z-50 bg-black/50 backdrop-blur-sm"
|
|
style="display: none;">
|
|
</div>
|
|
<!-- Modal -->
|
|
<div x-show="open"
|
|
x-transition:enter="transition ease-out duration-300"
|
|
x-transition:enter-start="opacity-0 scale-95"
|
|
x-transition:enter-end="opacity-100 scale-100"
|
|
x-transition:leave="transition ease-in duration-200"
|
|
x-transition:leave-start="opacity-100 scale-100"
|
|
x-transition:leave-end="opacity-0 scale-95"
|
|
@click.self="open = false"
|
|
@keydown.escape.window="open = false"
|
|
class="fixed inset-0 z-50 flex items-center justify-center p-4"
|
|
style="display: none;">
|
|
<div class="relative w-full max-w-2xl max-h-[90vh] overflow-y-auto bg-background-secondary rounded-xl border border-neutral/20 shadow-2xl p-6">
|
|
<button @click="open = false" class="absolute top-4 right-4 text-base/50 hover:text-base transition-colors">
|
|
<x-ri-close-line class="size-5" />
|
|
</button>
|
|
<div class="flex items-center gap-3 mb-4 pr-8">
|
|
<div class="flex items-center justify-center w-10 h-10 rounded-lg bg-primary/10">
|
|
<x-ri-bar-chart-2-line class="size-5 text-primary" />
|
|
</div>
|
|
<h6 class="font-sans text-lg font-semibold leading-tight">
|
|
Historical Data Comparison
|
|
</h6>
|
|
</div>
|
|
<p class="block font-sans text-sm font-normal leading-relaxed text-base/90 mb-4">
|
|
Compare current server statistics with historical data from a previous time period. This helps you identify trends, performance changes, and resource utilization patterns over time.
|
|
</p>
|
|
<div class="pt-4 mt-4 border-t border-neutral/30 space-y-2.5">
|
|
<p class="block font-sans text-xs font-normal leading-relaxed text-base/75">
|
|
<span class="font-semibold text-base/90">Period Selection:</span> Choose both a historical period (to compare against) and a current period (to compare from). You can select predefined time periods or use custom date/time ranges.
|
|
</p>
|
|
<p class="block font-sans text-xs font-normal leading-relaxed text-base/75">
|
|
<span class="font-semibold text-base/90">Metrics Compared:</span> All major statistics are compared including CPU usage, memory utilization, disk usage, network traffic rates (in/out), and disk I/O rates (read/write).
|
|
</p>
|
|
<p class="block font-sans text-xs font-normal leading-relaxed text-base/75">
|
|
<span class="font-semibold text-base/90">Visualizations:</span> Side-by-side comparisons with visual indicators show percentage changes, absolute differences, and trend directions for each metric.
|
|
</p>
|
|
<p class="block font-sans text-xs font-normal leading-relaxed text-base/75">
|
|
<span class="font-semibold text-base/90">Export:</span> Comparison data can be exported to CSV format for further analysis or reporting.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="flex items-center gap-2 shrink-0 flex-wrap">
|
|
<button type="button"
|
|
wire:click="exportHistoricalComparison"
|
|
class="bg-background border border-neutral px-3 py-2 rounded-md text-sm font-semibold hover:bg-background/80 flex items-center gap-2 h-[calc(0.5rem*2+1.25rem)]"
|
|
title="Export comparison data to CSV">
|
|
<x-ri-download-line class="size-4" />
|
|
Export
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="p-4 border-b border-neutral bg-background/50">
|
|
<!-- Period Selection -->
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-3">
|
|
<div class="flex items-center gap-2">
|
|
<label class="text-xs font-semibold text-base/70 whitespace-nowrap">Historical:</label>
|
|
<select wire:model.live="historicalPeriod" class="flex-1 bg-background border border-neutral rounded-md px-2 py-1.5 text-xs focus:outline-none focus:ring-2 focus:ring-secondary">
|
|
<option value="1_hour_ago">1 Hour Ago</option>
|
|
<option value="2_hours_ago">2 Hours Ago</option>
|
|
<option value="6_hours_ago">6 Hours Ago</option>
|
|
<option value="12_hours_ago">12 Hours Ago</option>
|
|
<option value="24_hours_ago">24 Hours Ago</option>
|
|
<option value="custom">Custom Date/Time</option>
|
|
</select>
|
|
@if($historicalPeriod === 'custom')
|
|
<input type="datetime-local" wire:model.live="historicalCustomDate" class="flex-1 bg-background border border-neutral rounded-md px-2 py-1.5 text-xs focus:outline-none focus:ring-2 focus:ring-secondary">
|
|
@endif
|
|
</div>
|
|
|
|
<div class="flex items-center gap-2">
|
|
<label class="text-xs font-semibold text-base/70 whitespace-nowrap">Current:</label>
|
|
<select wire:model.live="currentPeriod" class="flex-1 bg-background border border-neutral rounded-md px-2 py-1.5 text-xs focus:outline-none focus:ring-2 focus:ring-secondary">
|
|
<option value="now">Now (Latest)</option>
|
|
<option value="1_hour_ago">1 Hour Ago</option>
|
|
<option value="2_hours_ago">2 Hours Ago</option>
|
|
<option value="custom">Custom Date/Time</option>
|
|
</select>
|
|
@if($currentPeriod === 'custom')
|
|
<input type="datetime-local" wire:model.live="currentCustomDate" class="flex-1 bg-background border border-neutral rounded-md px-2 py-1.5 text-xs focus:outline-none focus:ring-2 focus:ring-secondary">
|
|
@endif
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
@if($historicalComparison && !isset($historicalComparison['error']))
|
|
<script type="application/json" data-statistics-json="period-compare-export" class="hidden">
|
|
@json($this->historicalComparisonExportData ?? [])
|
|
</script>
|
|
@php
|
|
$current = $historicalComparison['current'];
|
|
$historical = $historicalComparison['historical'];
|
|
$diff = $historicalComparison['differences'];
|
|
@endphp
|
|
|
|
<!-- Comparison Display -->
|
|
<div class="overflow-x-auto">
|
|
<table class="w-full">
|
|
<thead>
|
|
<tr class="border-b border-neutral bg-background">
|
|
<th class="text-left py-3 px-4 text-xs font-semibold text-base/70 uppercase tracking-wider whitespace-nowrap">Metric</th>
|
|
<th class="text-left py-3 px-4 text-xs font-semibold text-base/70 uppercase tracking-wider whitespace-nowrap">
|
|
<div class="flex items-center gap-2">
|
|
<x-ri-history-line class="size-3" />
|
|
Historical
|
|
<span class="text-xs font-normal normal-case text-base/50">({{ $historical->DateTime->format('M d, Y H:i') }})</span>
|
|
</div>
|
|
</th>
|
|
<th class="text-left py-3 px-4 text-xs font-semibold text-base/70 uppercase tracking-wider whitespace-nowrap">
|
|
<div class="flex items-center gap-2">
|
|
<x-ri-time-line class="size-3" />
|
|
Current
|
|
<span class="text-xs font-normal normal-case text-base/50">({{ $current->DateTime->format('M d, Y H:i') }})</span>
|
|
</div>
|
|
</th>
|
|
<th class="text-left py-3 px-4 text-xs font-semibold text-base/70 uppercase tracking-wider whitespace-nowrap">Change</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="divide-y divide-neutral/40">
|
|
<!-- Processor Utilization -->
|
|
<tr class="hover:bg-background-secondary/50 transition-colors">
|
|
<td class="py-3 px-4 text-sm whitespace-nowrap">
|
|
<div class="flex items-center gap-2">
|
|
<x-ri-cpu-line class="size-4 text-primary" />
|
|
<span class="font-medium">Processor Utilization</span>
|
|
</div>
|
|
</td>
|
|
<td class="py-3 px-4 text-sm whitespace-nowrap">
|
|
<span class="font-medium">{{ number_format($diff['cpu']['historical_percent'], 1) }}%</span>
|
|
</td>
|
|
<td class="py-3 px-4 text-sm whitespace-nowrap">
|
|
<span class="font-medium">{{ number_format($diff['cpu']['current_percent'], 1) }}%</span>
|
|
</td>
|
|
<td class="py-3 px-4 text-sm whitespace-nowrap">
|
|
<span class="inline-flex items-center px-2 py-1 rounded-md text-xs font-semibold {{ $diff['cpu']['percent_change'] > 0 ? 'bg-warning/20 text-warning' : ($diff['cpu']['percent_change'] < 0 ? 'bg-success/20 text-success' : 'bg-base/20 text-base/70') }}">
|
|
{{ $diff['cpu']['percent_change'] > 0 ? '+' : '' }}{{ number_format($diff['cpu']['percent_change'], 1) }}%
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
|
|
<!-- Memory Utilization -->
|
|
<tr class="hover:bg-background-secondary/50 transition-colors">
|
|
<td class="py-3 px-4 text-sm whitespace-nowrap">
|
|
<div class="flex items-center gap-2">
|
|
<x-ri-ram-line class="size-4 text-success" />
|
|
<span class="font-medium">Memory Utilization</span>
|
|
</div>
|
|
</td>
|
|
<td class="py-3 px-4 text-sm whitespace-nowrap">
|
|
<span class="font-medium">{{ number_format($diff['memory']['historical_percent'], 1) }}%</span>
|
|
</td>
|
|
<td class="py-3 px-4 text-sm whitespace-nowrap">
|
|
<span class="font-medium">{{ number_format($diff['memory']['current_percent'], 1) }}%</span>
|
|
</td>
|
|
<td class="py-3 px-4 text-sm whitespace-nowrap">
|
|
<span class="inline-flex items-center px-2 py-1 rounded-md text-xs font-semibold {{ $diff['memory']['percent_change'] > 0 ? 'bg-warning/20 text-warning' : ($diff['memory']['percent_change'] < 0 ? 'bg-success/20 text-success' : 'bg-base/20 text-base/70') }}">
|
|
{{ $diff['memory']['percent_change'] > 0 ? '+' : '' }}{{ number_format($diff['memory']['percent_change'], 1) }}%
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
|
|
<!-- Disk Read Rate -->
|
|
<tr class="hover:bg-background-secondary/50 transition-colors">
|
|
<td class="py-3 px-4 text-sm whitespace-nowrap">
|
|
<div class="flex items-center gap-2">
|
|
<x-ri-hard-drive-line class="size-4 text-purple-500 dark:text-purple-400" />
|
|
<span class="font-medium">Disk Read Rate</span>
|
|
</div>
|
|
</td>
|
|
<td class="py-3 px-4 text-sm whitespace-nowrap">
|
|
<span class="font-medium text-purple-500 dark:text-purple-400">{{ $this->formatBytes($diff['disk_read']['historical_rate']) }}/s</span>
|
|
</td>
|
|
<td class="py-3 px-4 text-sm whitespace-nowrap">
|
|
<span class="font-medium text-purple-500 dark:text-purple-400">{{ $this->formatBytes($diff['disk_read']['current_rate']) }}/s</span>
|
|
</td>
|
|
<td class="py-3 px-4 text-sm whitespace-nowrap">
|
|
<span class="inline-flex items-center px-2 py-1 rounded-md text-xs font-semibold {{ $diff['disk_read']['percent_change'] > 0 ? 'bg-warning/20 text-warning' : ($diff['disk_read']['percent_change'] < 0 ? 'bg-success/20 text-success' : 'bg-base/20 text-base/70') }}">
|
|
{{ $diff['disk_read']['percent_change'] > 0 ? '+' : '' }}{{ number_format($diff['disk_read']['percent_change'], 1) }}%
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
|
|
<!-- Disk Write Rate -->
|
|
<tr class="hover:bg-background-secondary/50 transition-colors">
|
|
<td class="py-3 px-4 text-sm whitespace-nowrap">
|
|
<div class="flex items-center gap-2">
|
|
<x-ri-hard-drive-line class="size-4 text-purple-700 dark:text-purple-300" />
|
|
<span class="font-medium">Disk Write Rate</span>
|
|
</div>
|
|
</td>
|
|
<td class="py-3 px-4 text-sm whitespace-nowrap">
|
|
<span class="font-medium text-purple-700 dark:text-purple-300">{{ $this->formatBytes($diff['disk_write']['historical_rate']) }}/s</span>
|
|
</td>
|
|
<td class="py-3 px-4 text-sm whitespace-nowrap">
|
|
<span class="font-medium text-purple-700 dark:text-purple-300">{{ $this->formatBytes($diff['disk_write']['current_rate']) }}/s</span>
|
|
</td>
|
|
<td class="py-3 px-4 text-sm whitespace-nowrap">
|
|
<span class="inline-flex items-center px-2 py-1 rounded-md text-xs font-semibold {{ $diff['disk_write']['percent_change'] > 0 ? 'bg-warning/20 text-warning' : ($diff['disk_write']['percent_change'] < 0 ? 'bg-success/20 text-success' : 'bg-base/20 text-base/70') }}">
|
|
{{ $diff['disk_write']['percent_change'] > 0 ? '+' : '' }}{{ number_format($diff['disk_write']['percent_change'], 1) }}%
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
|
|
<!-- Network Download Rate -->
|
|
<tr class="hover:bg-background-secondary/50 transition-colors">
|
|
<td class="py-3 px-4 text-sm whitespace-nowrap">
|
|
<div class="flex items-center gap-2">
|
|
<x-ri-arrow-left-right-line class="size-4 text-success" />
|
|
<span class="font-medium">Network Download Rate</span>
|
|
</div>
|
|
</td>
|
|
<td class="py-3 px-4 text-sm whitespace-nowrap">
|
|
<span class="font-medium text-success">{{ $this->formatBytes($diff['network_in']['historical_rate']) }}/s</span>
|
|
</td>
|
|
<td class="py-3 px-4 text-sm whitespace-nowrap">
|
|
<span class="font-medium text-success">{{ $this->formatBytes($diff['network_in']['current_rate']) }}/s</span>
|
|
</td>
|
|
<td class="py-3 px-4 text-sm whitespace-nowrap">
|
|
<span class="inline-flex items-center px-2 py-1 rounded-md text-xs font-semibold {{ $diff['network_in']['percent_change'] > 0 ? 'bg-warning/20 text-warning' : ($diff['network_in']['percent_change'] < 0 ? 'bg-success/20 text-success' : 'bg-base/20 text-base/70') }}">
|
|
{{ $diff['network_in']['percent_change'] > 0 ? '+' : '' }}{{ number_format($diff['network_in']['percent_change'], 1) }}%
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
|
|
<!-- Network Upload Rate -->
|
|
<tr class="hover:bg-background-secondary/50 transition-colors">
|
|
<td class="py-3 px-4 text-sm whitespace-nowrap">
|
|
<div class="flex items-center gap-2">
|
|
<x-ri-arrow-left-right-line class="size-4 text-info" />
|
|
<span class="font-medium">Network Upload Rate</span>
|
|
</div>
|
|
</td>
|
|
<td class="py-3 px-4 text-sm whitespace-nowrap">
|
|
<span class="font-medium text-info">{{ $this->formatBytes($diff['network_out']['historical_rate']) }}/s</span>
|
|
</td>
|
|
<td class="py-3 px-4 text-sm whitespace-nowrap">
|
|
<span class="font-medium text-info">{{ $this->formatBytes($diff['network_out']['current_rate']) }}/s</span>
|
|
</td>
|
|
<td class="py-3 px-4 text-sm whitespace-nowrap">
|
|
<span class="inline-flex items-center px-2 py-1 rounded-md text-xs font-semibold {{ $diff['network_out']['percent_change'] > 0 ? 'bg-warning/20 text-warning' : ($diff['network_out']['percent_change'] < 0 ? 'bg-success/20 text-success' : 'bg-base/20 text-base/70') }}">
|
|
{{ $diff['network_out']['percent_change'] > 0 ? '+' : '' }}{{ number_format($diff['network_out']['percent_change'], 1) }}%
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
@elseif($historicalComparison && isset($historicalComparison['error']))
|
|
<div class="p-6">
|
|
<div class="p-4 bg-warning/10 border border-warning/30 rounded-lg">
|
|
<div class="flex items-start gap-3">
|
|
<div class="p-2 bg-warning/20 rounded-full flex-shrink-0">
|
|
<x-ri-error-warning-line class="size-5 text-warning" />
|
|
</div>
|
|
<div>
|
|
<p class="text-sm font-semibold text-warning mb-1">{{ $historicalComparison['message'] }}</p>
|
|
@if(isset($historicalComparison['total_stats']))
|
|
<p class="text-xs text-warning/70">
|
|
Total statistics in database: {{ $historicalComparison['total_stats'] }}
|
|
@if($historicalComparison['total_stats'] == 0)
|
|
- Statistics are collected automatically every hour. You can also create manual statistics using the "Create Statistics" button above.
|
|
@elseif($historicalComparison['total_stats'] < 2)
|
|
- At least 2 statistics are needed for comparison. Wait for more data to be collected or create a manual statistic.
|
|
@endif
|
|
</p>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@else
|
|
<div class="text-center py-8">
|
|
<div class="flex flex-col items-center gap-2">
|
|
<div class="p-2 bg-base/10 rounded-full">
|
|
<x-ri-bar-chart-2-line class="size-5 text-base/60" />
|
|
</div>
|
|
<div>
|
|
<p class="text-sm font-semibold mb-1">No comparison data available</p>
|
|
<p class="text-xs text-base/60">Please select periods with available statistics data.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
|