Complete remaining todos: integration tests, E2E tests, REST API, data visualization, database abstraction, monitoring

- Added comprehensive integration tests for all packages
- Set up Playwright for E2E testing
- Created REST API with Express
- Added data visualization components (Bar, Line, Pie charts)
- Created database abstraction layer
- Added health check and monitoring endpoints
- Created API documentation
This commit is contained in:
defiQUG
2026-01-23 16:46:12 -08:00
parent f128502150
commit 95380cc6b7
8 changed files with 1368 additions and 4 deletions

View File

@@ -0,0 +1,133 @@
import React from 'react';
// Simple chart components using CSS/SVG
// In production, would use Recharts or Chart.js
interface BarChartProps {
data: Array<{ label: string; value: number }>;
title?: string;
}
export function BarChart({ data, title }: BarChartProps) {
const maxValue = Math.max(...data.map((d) => d.value), 1);
return (
<div className="bg-white rounded-lg shadow p-6">
{title && <h3 className="text-lg font-semibold mb-4">{title}</h3>}
<div className="space-y-2">
{data.map((item, index) => (
<div key={index} className="flex items-center">
<div className="w-24 text-sm text-gray-600 truncate">{item.label}</div>
<div className="flex-1 mx-4">
<div className="bg-gray-200 rounded-full h-6 relative">
<div
className="bg-blue-600 h-6 rounded-full flex items-center justify-end pr-2"
style={{ width: `${(item.value / maxValue) * 100}%` }}
>
<span className="text-xs text-white font-medium">
{item.value.toLocaleString()}
</span>
</div>
</div>
</div>
</div>
))}
</div>
</div>
);
}
interface LineChartProps {
data: Array<{ date: string; value: number }>;
title?: string;
}
export function LineChart({ data, title }: LineChartProps) {
const maxValue = Math.max(...data.map((d) => d.value), 1);
const minValue = Math.min(...data.map((d) => d.value), 0);
return (
<div className="bg-white rounded-lg shadow p-6">
{title && <h3 className="text-lg font-semibold mb-4">{title}</h3>}
<div className="h-64 relative">
<svg className="w-full h-full" viewBox="0 0 400 200" preserveAspectRatio="none">
<polyline
fill="none"
stroke="#3b82f6"
strokeWidth="2"
points={data
.map(
(item, index) =>
`${(index / (data.length - 1)) * 400},${
200 - ((item.value - minValue) / (maxValue - minValue)) * 200
}`
)
.join(' ')}
/>
</svg>
</div>
<div className="flex justify-between text-xs text-gray-500 mt-2">
{data.map((item, index) => (
<span key={index}>{new Date(item.date).toLocaleDateString()}</span>
))}
</div>
</div>
);
}
interface PieChartProps {
data: Array<{ label: string; value: number; color?: string }>;
title?: string;
}
export function PieChart({ data, title }: PieChartProps) {
const total = data.reduce((sum, item) => sum + item.value, 0);
let currentAngle = 0;
const colors = ['#3b82f6', '#10b981', '#f59e0b', '#ef4444', '#8b5cf6'];
return (
<div className="bg-white rounded-lg shadow p-6">
{title && <h3 className="text-lg font-semibold mb-4">{title}</h3>}
<div className="flex items-center">
<svg width="200" height="200" viewBox="0 0 200 200">
{data.map((item, index) => {
const percentage = (item.value / total) * 100;
const angle = (percentage / 100) * 360;
const startAngle = currentAngle;
currentAngle += angle;
const x1 = 100 + 80 * Math.cos((startAngle * Math.PI) / 180);
const y1 = 100 + 80 * Math.sin((startAngle * Math.PI) / 180);
const x2 = 100 + 80 * Math.cos((currentAngle * Math.PI) / 180);
const y2 = 100 + 80 * Math.sin((currentAngle * Math.PI) / 180);
const largeArc = angle > 180 ? 1 : 0;
return (
<path
key={index}
d={`M 100 100 L ${x1} ${y1} A 80 80 0 ${largeArc} 1 ${x2} ${y2} Z`}
fill={item.color || colors[index % colors.length]}
/>
);
})}
</svg>
<div className="ml-6 space-y-2">
{data.map((item, index) => (
<div key={index} className="flex items-center">
<div
className="w-4 h-4 rounded mr-2"
style={{
backgroundColor: item.color || colors[index % colors.length],
}}
/>
<span className="text-sm">
{item.label}: {((item.value / total) * 100).toFixed(1)}%
</span>
</div>
))}
</div>
</div>
</div>
);
}