import { ReactNode } from 'react'; import Switch from '../form/switch/Switch'; import Button from '../ui/button/Button'; import { usePersistentToggle } from '../../hooks/usePersistentToggle'; import { useToast } from '../ui/toast/ToastContainer'; type ValidationStatus = 'not_configured' | 'pending' | 'success' | 'error'; interface IntegrationCardProps { icon: ReactNode; title: string; description: string; enabled?: boolean; // Optional - if not provided, will use persistent toggle hook validationStatus: ValidationStatus; // 'not_configured' | 'pending' | 'success' | 'error' onToggle?: (enabled: boolean) => void; // Optional - if not provided, will use persistent toggle hook onSettings: () => void; onDetails: () => void; // Optional props for built-in persistence integrationId?: string; // If provided, enables built-in persistence getEndpoint?: string; // API endpoint pattern for loading (default if integrationId provided) saveEndpoint?: string; // API endpoint pattern for saving (default if integrationId provided) onToggleSuccess?: (enabled: boolean, data?: any) => void; // Callback when toggle succeeds - receives enabled state and full config data onToggleError?: (error: Error) => void; // Callback when toggle fails modelName?: string; // For Runware: display model name instead of status circle } export default function IntegrationCard({ icon, title, description, enabled: externalEnabled, validationStatus, onToggle: externalOnToggle, onSettings, onDetails, integrationId, getEndpoint, saveEndpoint, onToggleSuccess: externalOnToggleSuccess, onToggleError: externalOnToggleError, modelName, }: IntegrationCardProps) { const toast = useToast(); // Use built-in persistent toggle if integrationId is provided // This hook automatically loads state on mount and saves on toggle // When using built-in persistence, we IGNORE external enabled prop to avoid conflicts const persistentToggle = integrationId ? usePersistentToggle({ resourceId: integrationId, getEndpoint: getEndpoint || '/v1/system/settings/integrations/{id}/', saveEndpoint: saveEndpoint || '/v1/system/settings/integrations/{id}/save/', initialEnabled: false, // Always start with false, let hook load from API onToggleSuccess: (enabled, data) => { // Show success toast toast.success(`${integrationId} ${enabled ? 'enabled' : 'disabled'}`); // Call external callbacks if provided - pass both enabled state and full config data if (externalOnToggleSuccess) { externalOnToggleSuccess(enabled, data); } // Don't call external onToggle when using built-in persistence // The hook manages its own state, parent should not interfere }, onToggleError: (error) => { toast.error(`Failed to update ${integrationId}: ${error.message}`); if (externalOnToggleError) { externalOnToggleError(error); } }, }) : null; // Determine which enabled state and toggle function to use // When integrationId is provided, hook is the SINGLE source of truth // When not provided, use external prop (backwards compatible) const enabled = persistentToggle ? persistentToggle.enabled : (externalEnabled ?? false); const handleToggle = persistentToggle ? (newEnabled: boolean) => { // Built-in persistence - automatically saves to backend persistentToggle.toggle(newEnabled); } : (newEnabled: boolean) => { // External handler mode - parent manages state if (externalOnToggle) { externalOnToggle(newEnabled); } }; const isToggling = persistentToggle ? persistentToggle.loading : false; // Determine status circle color const getStatusColor = () => { if (!enabled || validationStatus === 'not_configured') { return 'bg-gray-400 dark:bg-gray-500'; // Grey for disabled or not configured } if (validationStatus === 'pending') { return 'bg-gray-400 dark:bg-gray-500 animate-pulse'; // Grey while validating (with pulse) } if (validationStatus === 'success') { return 'bg-success-500 dark:bg-success-600'; // Green for success } if (validationStatus === 'error') { return 'bg-error-500 dark:bg-error-600'; // Red for error } return 'bg-gray-400 dark:bg-gray-500'; // Default grey }; // Get status text and color const getStatusText = () => { if (!enabled || validationStatus === 'not_configured') { return { text: 'Disabled', color: 'text-gray-400 dark:text-gray-500', bold: false }; } if (validationStatus === 'pending') { return { text: 'Pending', color: 'text-gray-400 dark:text-gray-500', bold: false }; } if (validationStatus === 'success') { return { text: 'Enabled', color: 'text-gray-800 dark:text-white', bold: true }; } if (validationStatus === 'error') { return { text: 'Error', color: 'text-error-600 dark:text-error-400', bold: false }; } return { text: 'Disabled', color: 'text-gray-400 dark:text-gray-500', bold: false }; }; const statusText = getStatusText(); return (
{icon}

{title}

{description}

{/* Status Text and Circle - Same row */} {/* For Runware: Show model name instead of circle */} {integrationId === 'runware' ? (
{modelName || 'Disabled'}
) : (
{statusText.text}
)}
); }