- Add auto-deploy.sh script with full CI/CD pipeline - Add quick-deploy.sh for fast development deployments - Add Git post-receive hook for automatic deployment on push - Add comprehensive deployment documentation - Add npm scripts for easy deployment management - Include health checks, logging, and cleanup - Support for automatic rollback on failures
140 lines
4.4 KiB
TypeScript
140 lines
4.4 KiB
TypeScript
'use client';
|
|
|
|
import { useState, useEffect } from 'react';
|
|
import { trackEvent } from '@/lib/analytics';
|
|
|
|
interface PerformanceData {
|
|
timestamp: string;
|
|
url: string;
|
|
metrics: {
|
|
LCP?: number;
|
|
FID?: number;
|
|
CLS?: number;
|
|
FCP?: number;
|
|
TTFB?: number;
|
|
};
|
|
}
|
|
|
|
export const PerformanceDashboard: React.FC = () => {
|
|
const [performanceData, setPerformanceData] = useState<PerformanceData[]>([]);
|
|
const [isVisible, setIsVisible] = useState(false);
|
|
|
|
useEffect(() => {
|
|
// This would typically fetch from your Umami instance or database
|
|
// For now, we'll show a placeholder
|
|
const mockData: PerformanceData[] = [
|
|
{
|
|
timestamp: new Date().toISOString(),
|
|
url: '/',
|
|
metrics: {
|
|
LCP: 1200,
|
|
FID: 45,
|
|
CLS: 0.1,
|
|
FCP: 800,
|
|
TTFB: 200,
|
|
},
|
|
},
|
|
];
|
|
setPerformanceData(mockData);
|
|
}, []);
|
|
|
|
const getPerformanceGrade = (metric: string, value: number): string => {
|
|
switch (metric) {
|
|
case 'LCP':
|
|
return value <= 2500 ? 'Good' : value <= 4000 ? 'Needs Improvement' : 'Poor';
|
|
case 'FID':
|
|
return value <= 100 ? 'Good' : value <= 300 ? 'Needs Improvement' : 'Poor';
|
|
case 'CLS':
|
|
return value <= 0.1 ? 'Good' : value <= 0.25 ? 'Needs Improvement' : 'Poor';
|
|
case 'FCP':
|
|
return value <= 1800 ? 'Good' : value <= 3000 ? 'Needs Improvement' : 'Poor';
|
|
case 'TTFB':
|
|
return value <= 800 ? 'Good' : value <= 1800 ? 'Needs Improvement' : 'Poor';
|
|
default:
|
|
return 'Unknown';
|
|
}
|
|
};
|
|
|
|
const getGradeColor = (grade: string): string => {
|
|
switch (grade) {
|
|
case 'Good':
|
|
return 'text-green-600 bg-green-100';
|
|
case 'Needs Improvement':
|
|
return 'text-yellow-600 bg-yellow-100';
|
|
case 'Poor':
|
|
return 'text-red-600 bg-red-100';
|
|
default:
|
|
return 'text-gray-600 bg-gray-100';
|
|
}
|
|
};
|
|
|
|
if (!isVisible) {
|
|
return (
|
|
<button
|
|
onClick={() => {
|
|
setIsVisible(true);
|
|
trackEvent('dashboard-toggle', { action: 'show' });
|
|
}}
|
|
className="fixed bottom-4 right-4 bg-blue-600 text-white px-4 py-2 rounded-lg shadow-lg hover:bg-blue-700 transition-colors z-50"
|
|
>
|
|
📊 Performance
|
|
</button>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="fixed bottom-4 right-4 bg-white border border-gray-200 rounded-lg shadow-xl p-6 w-96 max-h-96 overflow-y-auto z-50">
|
|
<div className="flex justify-between items-center mb-4">
|
|
<h3 className="text-lg font-semibold text-gray-800">Performance Dashboard</h3>
|
|
<button
|
|
onClick={() => {
|
|
setIsVisible(false);
|
|
trackEvent('dashboard-toggle', { action: 'hide' });
|
|
}}
|
|
className="text-gray-500 hover:text-gray-700"
|
|
>
|
|
✕
|
|
</button>
|
|
</div>
|
|
|
|
<div className="space-y-4">
|
|
{performanceData.map((data, index) => (
|
|
<div key={index} className="border-b border-gray-100 pb-4">
|
|
<div className="text-sm text-gray-600 mb-2">
|
|
{new Date(data.timestamp).toLocaleString()}
|
|
</div>
|
|
<div className="text-sm font-medium text-gray-800 mb-2">
|
|
{data.url}
|
|
</div>
|
|
|
|
<div className="grid grid-cols-2 gap-2">
|
|
{Object.entries(data.metrics).map(([metric, value]) => {
|
|
const grade = getPerformanceGrade(metric, value);
|
|
return (
|
|
<div key={metric} className="flex justify-between items-center">
|
|
<span className="text-xs font-medium text-gray-600">{metric}:</span>
|
|
<div className="flex items-center space-x-2">
|
|
<span className="text-xs font-mono">{value}ms</span>
|
|
<span className={`text-xs px-2 py-1 rounded ${getGradeColor(grade)}`}>
|
|
{grade}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
<div className="mt-4 pt-4 border-t border-gray-100">
|
|
<div className="text-xs text-gray-500">
|
|
<div>🟢 Good: Meets recommended thresholds</div>
|
|
<div>🟡 Needs Improvement: Below recommended thresholds</div>
|
|
<div>🔴 Poor: Significantly below thresholds</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|