Redesign StatsPage header with improved UI/UX
- Replace old header design with modern card-based layout - Add color-coded metric cards with FontAwesome icons - Improve time period selector with pill-style buttons - Replace custom SVG icons with FontAwesome icons (fa-chart-bar, fa-bolt, fa-database, fa-download, fa-arrow-left) - Enhance visual hierarchy with proper spacing and gradients - Fix mobile responsiveness for duration selector (flex-wrap support) - Simplify CSS by removing redundant properties - Merge summary and time selector sections for better flow
This commit is contained in:
@@ -61,49 +61,111 @@ export const StatsPage: React.FC<StatsPageProps> = ({
|
||||
}));
|
||||
|
||||
return (
|
||||
<div className="mx-auto max-w-7xl px-4 mt-6 sm:mt-8">
|
||||
<h1 className="text-2xl font-bold text-slate-900 text-center">HTTP Request Log Stats</h1>
|
||||
<div className="mt-2 text-center">
|
||||
<a
|
||||
href="/log_entries"
|
||||
className="text-blue-600 hover:text-blue-800 transition-colors"
|
||||
>
|
||||
Back to Index
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className="mt-6 text-center">
|
||||
{/* Time Window Selector */}
|
||||
<div className="space-x-2">
|
||||
{availableTimeWindows.map((timeWindowOption) => (
|
||||
<React.Fragment key={timeWindowOption.seconds}>
|
||||
{timeWindowOption.active ? (
|
||||
<span className="rounded-full bg-blue-100 px-3 py-1 text-sm font-medium text-blue-800">
|
||||
{timeWindowOption.label}
|
||||
</span>
|
||||
) : (
|
||||
<a
|
||||
href={timeWindowOption.path}
|
||||
className="rounded-full px-3 py-1 text-sm text-blue-600 hover:text-blue-800 hover:bg-blue-50 transition-colors"
|
||||
>
|
||||
{timeWindowOption.label}
|
||||
</a>
|
||||
)}
|
||||
</React.Fragment>
|
||||
))}
|
||||
<div className="mx-auto max-w-7xl px-4 mt-8">
|
||||
{/* Header Section */}
|
||||
<div className="bg-white border border-slate-200 rounded-xl shadow-sm overflow-hidden">
|
||||
{/* Top Bar */}
|
||||
<div className="bg-slate-50 px-6 py-4 border-b border-slate-200">
|
||||
<div className="flex items-center justify-between">
|
||||
<h1 className="text-2xl font-bold text-slate-900">HTTP Request Analytics</h1>
|
||||
<a
|
||||
href="/log_entries"
|
||||
className="inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-slate-600 hover:text-slate-900 hover:bg-white border border-slate-300 rounded-lg transition-colors hover:shadow-sm"
|
||||
>
|
||||
<i className="fas fa-arrow-left" />
|
||||
Back to Log Entries
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<StatsCard
|
||||
requestCount={lastWindowCount}
|
||||
timeWindow={timeWindowFormatted}
|
||||
requestsPerSecond={requestsPerSecond}
|
||||
totalBytes={totalBytesFormatted}
|
||||
bytesPerSecond={bytesPerSecondFormatted}
|
||||
/>
|
||||
{/* Stats Summary */}
|
||||
<div className="px-6 py-6 bg-gradient-to-br from-blue-50 to-indigo-50 border-t border-slate-200">
|
||||
<div className="text-center mb-4">
|
||||
<h3 className="text-lg font-semibold text-slate-900 mb-1">Summary for {timeWindowFormatted}</h3>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
{/* Total Requests */}
|
||||
<div className="bg-white rounded-lg p-4 shadow-sm border border-slate-200">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-xs font-medium text-slate-500 uppercase tracking-wide">Total Requests</p>
|
||||
<p className="text-2xl font-bold text-slate-900 mt-1">{lastWindowCount.toLocaleString()}</p>
|
||||
</div>
|
||||
<div className="w-10 h-10 bg-blue-100 rounded-lg flex items-center justify-center">
|
||||
<i className="fas fa-chart-bar text-blue-600" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Requests per Second */}
|
||||
<div className="bg-white rounded-lg p-4 shadow-sm border border-slate-200">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-xs font-medium text-slate-500 uppercase tracking-wide">Requests/sec</p>
|
||||
<p className="text-2xl font-bold text-slate-900 mt-1">{requestsPerSecond}</p>
|
||||
</div>
|
||||
<div className="w-10 h-10 bg-green-100 rounded-lg flex items-center justify-center">
|
||||
<i className="fas fa-bolt text-green-600" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Total Data */}
|
||||
<div className="bg-white rounded-lg p-4 shadow-sm border border-slate-200">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-xs font-medium text-slate-500 uppercase tracking-wide">Total Data</p>
|
||||
<p className="text-2xl font-bold text-slate-900 mt-1">{totalBytesFormatted}</p>
|
||||
</div>
|
||||
<div className="w-10 h-10 bg-purple-100 rounded-lg flex items-center justify-center">
|
||||
<i className="fas fa-database text-purple-600" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Data per Second */}
|
||||
<div className="bg-white rounded-lg p-4 shadow-sm border border-slate-200">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-xs font-medium text-slate-500 uppercase tracking-wide">Data/sec</p>
|
||||
<p className="text-2xl font-bold text-slate-900 mt-1">{bytesPerSecondFormatted}</p>
|
||||
</div>
|
||||
<div className="w-10 h-10 bg-orange-100 rounded-lg flex items-center justify-center">
|
||||
<i className="fas fa-download text-orange-600" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Time Window Selector */}
|
||||
<div className="px-4 py-4 bg-gradient-to-br from-blue-50 to-indigo-50">
|
||||
<div className="text-center">
|
||||
<div className="inline-flex flex-wrap justify-center bg-white/70 backdrop-blur-sm rounded-lg p-1 shadow-md border border-white/50 gap-1">
|
||||
{availableTimeWindows.map((timeWindowOption, index) => (
|
||||
<React.Fragment key={timeWindowOption.seconds}>
|
||||
{timeWindowOption.active ? (
|
||||
<span className="px-4 py-2 text-sm font-semibold text-white bg-blue-600 rounded-md shadow-sm">
|
||||
{timeWindowOption.label}
|
||||
</span>
|
||||
) : (
|
||||
<a
|
||||
href={timeWindowOption.path}
|
||||
className="px-4 py-2 text-sm font-medium text-slate-600 hover:text-slate-900 hover:bg-white hover:shadow-sm rounded-md transition-colors border border-transparent hover:border-slate-200"
|
||||
>
|
||||
{timeWindowOption.label}
|
||||
</a>
|
||||
)}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Tables Grid - 2 columns */}
|
||||
<div className="mt-8 mb-8 grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||
<div className="my-8 grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||
<div>
|
||||
<h2 className="text-xl font-bold text-slate-900 mb-3">By Content Type</h2>
|
||||
<SortableTable
|
||||
|
||||
Reference in New Issue
Block a user