mirror of
https://github.com/stackblitz-labs/bolt.diy
synced 2025-03-10 22:21:44 +00:00
# UI V3 Changelog Major updates and improvements in this release: ## Core Changes - Complete NEW REWRITTEN UI system overhaul (V3) with semantic design tokens - New settings management system with drag-and-drop capabilities - Enhanced provider system supporting multiple AI services - Improved theme system with better dark mode support - New component library with consistent design patterns ## Technical Updates - Reorganized project architecture for better maintainability - Performance optimizations and bundle size improvements - Enhanced security features and access controls - Improved developer experience with better tooling - Comprehensive testing infrastructure ## New Features - Background rays effect for improved visual feedback - Advanced tab management system - Automatic and manual update support - Enhanced error handling and visualization - Improved accessibility across all components For detailed information about all changes and improvements, please see the full changelog.
136 lines
4.4 KiB
TypeScript
136 lines
4.4 KiB
TypeScript
import { useState, useEffect } from 'react';
|
|
import type { ServiceStatus } from './types';
|
|
import { ProviderStatusCheckerFactory } from './provider-factory';
|
|
|
|
export default function ServiceStatusTab() {
|
|
const [serviceStatuses, setServiceStatuses] = useState<ServiceStatus[]>([]);
|
|
const [loading, setLoading] = useState(true);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
useEffect(() => {
|
|
const checkAllProviders = async () => {
|
|
try {
|
|
setLoading(true);
|
|
setError(null);
|
|
|
|
const providers = ProviderStatusCheckerFactory.getProviderNames();
|
|
const statuses: ServiceStatus[] = [];
|
|
|
|
for (const provider of providers) {
|
|
try {
|
|
const checker = ProviderStatusCheckerFactory.getChecker(provider);
|
|
const result = await checker.checkStatus();
|
|
|
|
statuses.push({
|
|
provider,
|
|
...result,
|
|
lastChecked: new Date().toISOString(),
|
|
});
|
|
} catch (err) {
|
|
console.error(`Error checking ${provider} status:`, err);
|
|
statuses.push({
|
|
provider,
|
|
status: 'degraded',
|
|
message: 'Unable to check service status',
|
|
incidents: ['Error checking service status'],
|
|
lastChecked: new Date().toISOString(),
|
|
});
|
|
}
|
|
}
|
|
|
|
setServiceStatuses(statuses);
|
|
} catch (err) {
|
|
console.error('Error checking provider statuses:', err);
|
|
setError('Failed to check service statuses');
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
checkAllProviders();
|
|
|
|
// Set up periodic checks every 5 minutes
|
|
const interval = setInterval(checkAllProviders, 5 * 60 * 1000);
|
|
|
|
return () => clearInterval(interval);
|
|
}, []);
|
|
|
|
const getStatusColor = (status: ServiceStatus['status']) => {
|
|
switch (status) {
|
|
case 'operational':
|
|
return 'text-green-500 dark:text-green-400';
|
|
case 'degraded':
|
|
return 'text-yellow-500 dark:text-yellow-400';
|
|
case 'down':
|
|
return 'text-red-500 dark:text-red-400';
|
|
default:
|
|
return 'text-gray-500 dark:text-gray-400';
|
|
}
|
|
};
|
|
|
|
const getStatusIcon = (status: ServiceStatus['status']) => {
|
|
switch (status) {
|
|
case 'operational':
|
|
return 'i-ph:check-circle';
|
|
case 'degraded':
|
|
return 'i-ph:warning';
|
|
case 'down':
|
|
return 'i-ph:x-circle';
|
|
default:
|
|
return 'i-ph:question';
|
|
}
|
|
};
|
|
|
|
if (loading) {
|
|
return (
|
|
<div className="flex items-center justify-center h-full">
|
|
<div className="animate-spin i-ph:circle-notch w-8 h-8 text-purple-500" />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (error) {
|
|
return (
|
|
<div className="flex flex-col items-center justify-center h-full text-red-500 dark:text-red-400">
|
|
<div className="i-ph:warning w-8 h-8 mb-2" />
|
|
<p>{error}</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
<div className="grid grid-cols-1 gap-4">
|
|
{serviceStatuses.map((service) => (
|
|
<div
|
|
key={service.provider}
|
|
className="p-4 bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700"
|
|
>
|
|
<div className="flex items-center justify-between mb-2">
|
|
<h3 className="text-lg font-semibold text-gray-900 dark:text-white">{service.provider}</h3>
|
|
<div className={`flex items-center ${getStatusColor(service.status)}`}>
|
|
<div className={`${getStatusIcon(service.status)} w-5 h-5 mr-2`} />
|
|
<span className="capitalize">{service.status}</span>
|
|
</div>
|
|
</div>
|
|
<p className="text-gray-600 dark:text-gray-300 mb-2">{service.message}</p>
|
|
{service.incidents && service.incidents.length > 0 && (
|
|
<div className="mt-2">
|
|
<h4 className="text-sm font-semibold text-gray-700 dark:text-gray-300 mb-1">Recent Incidents:</h4>
|
|
<ul className="text-sm text-gray-600 dark:text-gray-400 space-y-1">
|
|
{service.incidents.map((incident, index) => (
|
|
<li key={index}>{incident}</li>
|
|
))}
|
|
</ul>
|
|
</div>
|
|
)}
|
|
<div className="mt-2 text-xs text-gray-500 dark:text-gray-400">
|
|
Last checked: {new Date(service.lastChecked).toLocaleString()}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|