🚀 Add automatic deployment system
- 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
This commit is contained in:
139
components/PerformanceDashboard.tsx
Normal file
139
components/PerformanceDashboard.tsx
Normal file
@@ -0,0 +1,139 @@
|
||||
'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>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user