- Next.js 14+ with App Router and TypeScript - Tailwind CSS and ShadCN UI styling - Zustand state management - Dexie.js for IndexedDB (local-first data) - Auth.js v5 for authentication - BMAD framework integration Co-Authored-By: Claude <noreply@anthropic.com>
96 lines
2.7 KiB
TypeScript
96 lines
2.7 KiB
TypeScript
"use client";
|
|
|
|
import { useState } from 'react';
|
|
import { Button } from '@/components/ui/button';
|
|
import { SettingsService } from '@/services/settings-service';
|
|
import { useApiKey } from '@/store/use-settings';
|
|
|
|
/**
|
|
* Connection Validation States
|
|
*/
|
|
type ValidationStatus = 'idle' | 'testing' | 'success' | 'error';
|
|
|
|
/**
|
|
* Connection Status Component (Story 4.2 Enhanced)
|
|
*
|
|
* Displays API connection validation status with detailed error messages
|
|
* and retry capability.
|
|
*/
|
|
export function ConnectionStatus() {
|
|
const apiKey = useApiKey();
|
|
const [status, setStatus] = useState<ValidationStatus>('idle');
|
|
const [errorMessage, setErrorMessage] = useState<string>('');
|
|
|
|
const handleTest = async () => {
|
|
if (!apiKey) return;
|
|
|
|
setStatus('testing');
|
|
setErrorMessage('');
|
|
|
|
try {
|
|
// Use SettingsService for proper Logic Sandwich pattern
|
|
const result = await SettingsService.validateProviderConnection();
|
|
|
|
if (result.isValid) {
|
|
setStatus('success');
|
|
} else {
|
|
setStatus('error');
|
|
setErrorMessage(result.error || 'Connection failed');
|
|
}
|
|
} catch (error) {
|
|
setStatus('error');
|
|
setErrorMessage(error instanceof Error ? error.message : 'Connection failed');
|
|
}
|
|
};
|
|
|
|
if (!apiKey) return null;
|
|
|
|
return (
|
|
<div className="space-y-2 mt-4">
|
|
<div className="flex items-center gap-4">
|
|
<Button
|
|
variant="outline"
|
|
onClick={handleTest}
|
|
disabled={status === 'testing'}
|
|
>
|
|
{status === 'testing' ? 'Testing...' : 'Test Connection'}
|
|
</Button>
|
|
|
|
{status === 'success' && (
|
|
<span className="text-green-600 font-medium flex items-center gap-1">
|
|
<span className="inline-block w-2 h-2 bg-green-500 rounded-full" />
|
|
Connected ✅
|
|
</span>
|
|
)}
|
|
|
|
{status === 'error' && (
|
|
<span className="text-red-600 font-medium flex items-center gap-1">
|
|
<span className="inline-block w-2 h-2 bg-red-500 rounded-full" />
|
|
Connection Failed ❌
|
|
</span>
|
|
)}
|
|
</div>
|
|
|
|
{status === 'error' && errorMessage && (
|
|
<div className="space-y-2">
|
|
<p className="text-sm text-red-600">{errorMessage}</p>
|
|
|
|
{/* Retry hint for network errors */}
|
|
{errorMessage.toLowerCase().includes('network') && (
|
|
<p className="text-xs text-muted-foreground">
|
|
Tip: Network errors can be temporary. Try again in a moment.
|
|
</p>
|
|
)}
|
|
</div>
|
|
)}
|
|
|
|
{/* Success message with auto-hide hint */}
|
|
{status === 'success' && (
|
|
<p className="text-xs text-green-600">
|
|
Your API credentials are working correctly!
|
|
</p>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|