/** * OAuth Token Status Component * Compact terminal-themed component for displaying OAuth token monitoring status * with platform-specific execution logs in expanded sections */ import React, { useState, useEffect } from 'react'; import { Box, Typography, IconButton, Tooltip, CircularProgress, Collapse, Table, TableBody, TableCell, TableHead, TableRow, Chip, Divider, } from '@mui/material'; import { RefreshCw, CheckCircle, XCircle, AlertTriangle, Info, ChevronDown, ChevronUp, } from 'lucide-react'; import { useAuth } from '@clerk/clerk-react'; import { getOAuthTokenStatus, manualRefreshToken, getOAuthTokenExecutionLogs, OAuthTokenStatusResponse, ManualRefreshResponse, ExecutionLog, ExecutionLogsResponse, } from '../../api/oauthTokenMonitoring'; import { TerminalPaper, TerminalTypography, TerminalChip, TerminalChipSuccess, TerminalChipError, TerminalChipWarning, TerminalAlert, TerminalTableCell, TerminalTableRow, terminalColors, } from './terminalTheme'; interface OAuthTokenStatusProps { compact?: boolean; } interface PlatformLogs { [platform: string]: { logs: ExecutionLog[]; loading: boolean; error: string | null; }; } const OAuthTokenStatus: React.FC = ({ compact = true }) => { const { userId } = useAuth(); const [status, setStatus] = useState(null); const [loading, setLoading] = useState(true); const [refreshing, setRefreshing] = useState(null); const [error, setError] = useState(null); const [expandedPlatform, setExpandedPlatform] = useState(null); const [platformLogs, setPlatformLogs] = useState({}); const [hoveredLogId, setHoveredLogId] = useState(null); const fetchStatus = async () => { if (!userId) return; try { setLoading(true); setError(null); const response = await getOAuthTokenStatus(userId); setStatus(response); } catch (err: any) { setError(err.message || 'Failed to fetch token status'); console.error('Error fetching OAuth token status:', err); } finally { setLoading(false); } }; const fetchPlatformLogs = async (platform: string) => { if (!userId) return; // Initialize platform logs state if not exists if (!platformLogs[platform]) { setPlatformLogs(prev => ({ ...prev, [platform]: { logs: [], loading: false, error: null } })); } setPlatformLogs(prev => ({ ...prev, [platform]: { ...prev[platform], loading: true, error: null } })); try { const response = await getOAuthTokenExecutionLogs(userId, platform, 10, 0); // Get latest 10 logs if (response.success && response.data) { setPlatformLogs(prev => ({ ...prev, [platform]: { logs: response.data.logs || [], loading: false, error: null } })); } } catch (err: any) { setPlatformLogs(prev => ({ ...prev, [platform]: { ...prev[platform], loading: false, error: err.message || 'Failed to fetch logs' } })); console.error(`Error fetching logs for ${platform}:`, err); } }; useEffect(() => { fetchStatus(); // Poll for status updates every 2 minutes const interval = setInterval(fetchStatus, 120000); return () => clearInterval(interval); }, [userId]); // Fetch logs when platform is expanded useEffect(() => { if (expandedPlatform && userId) { fetchPlatformLogs(expandedPlatform); } }, [expandedPlatform, userId]); const handleRefresh = async (platform: string) => { if (!userId) return; try { setRefreshing(platform); setError(null); const response: ManualRefreshResponse = await manualRefreshToken(userId, platform); // Refresh status after manual refresh await fetchStatus(); // Refresh logs if platform is expanded if (expandedPlatform === platform) { await fetchPlatformLogs(platform); } if (response.success) { console.log(`Token refresh successful for ${platform}`); } else { console.error(`Token refresh failed for ${platform}:`, response.data.execution_result.error_message); } } catch (err: any) { setError(err.message || `Failed to refresh ${platform} token`); console.error(`Error refreshing ${platform} token:`, err); } finally { setRefreshing(null); } }; const handleExpandPlatform = (platform: string) => { if (expandedPlatform === platform) { setExpandedPlatform(null); } else { setExpandedPlatform(platform); } }; const getStatusIcon = (taskStatus: string | null, connected: boolean) => { if (!connected) { return ; } if (!taskStatus || taskStatus === 'not_created') { return ; } switch (taskStatus) { case 'active': return ; case 'failed': return ; case 'paused': return ; default: return ; } }; const getStatusChip = (taskStatus: string | null, connected: boolean) => { if (!connected) { return ; } if (!taskStatus || taskStatus === 'not_created') { return ; } switch (taskStatus) { case 'active': return ; case 'failed': return ; case 'paused': return ; default: return ; } }; const formatDate = (dateString: string | null) => { if (!dateString) return 'Never'; try { const date = new Date(dateString); return date.toLocaleString(); } catch { return dateString; } }; const getPlatformDisplayName = (platform: string) => { const names: { [key: string]: string } = { gsc: 'GSC', bing: 'Bing', wordpress: 'WP', wix: 'Wix', }; return names[platform] || platform.toUpperCase(); }; const getLogStatusChip = (logStatus: string) => { switch (logStatus) { case 'success': return ; case 'failed': return ; case 'running': return ; default: return ; } }; const formatLogResult = (resultData: any): string => { if (!resultData) return 'N/A'; if (typeof resultData === 'string') { try { resultData = JSON.parse(resultData); } catch { return resultData.substring(0, 50); } } if (resultData.token_status) { return `Token: ${resultData.token_status}`; } if (resultData.platform) { return `Platform: ${resultData.platform}`; } const str = JSON.stringify(resultData); return str.length > 60 ? str.substring(0, 60) + '...' : str; }; if (loading && !status) { return ( ); } if (!status) { return null; } const platforms = ['gsc', 'bing', 'wordpress', 'wix']; return ( OAuth Token Status {error && ( setError(null)}> {error} )} Platform Status Last Check Actions {platforms.map((platform) => { const platformStatus = status.data.platform_status[platform]; const task = platformStatus?.monitoring_task; const isExpanded = expandedPlatform === platform; const logs = platformLogs[platform]; return ( {getStatusIcon(task?.status || null, platformStatus?.connected || false)} {getPlatformDisplayName(platform)} {getStatusChip(task?.status || null, platformStatus?.connected || false)} {task?.last_success && ( )} {task?.next_check && ( )} {formatDate(task?.last_check || null)} handleExpandPlatform(platform)} sx={{ color: terminalColors.primary, '&:hover': { backgroundColor: 'rgba(0, 255, 0, 0.1)', } }} > {isExpanded ? : } {platformStatus?.connected && ( handleRefresh(platform)} disabled={refreshing === platform} sx={{ color: terminalColors.primary, '&:hover': { backgroundColor: 'rgba(0, 255, 0, 0.1)', }, '&:disabled': { color: '#004400', } }} > {refreshing === platform ? ( ) : ( )} )} {task?.failure_reason && ( Last Failure: {task.failure_reason} {formatDate(task.last_failure || null)} )} {/* OAuth Monitoring Logs Section */} {platformStatus?.connected && ( <> 🔐 Monitoring Logs {logs?.loading ? ( Loading logs... ) : logs?.error ? ( {logs.error} ) : logs?.logs && logs.logs.length > 0 ? (
Date Status Result Duration {logs.logs.map((log) => ( setHoveredLogId(log.id)} onMouseLeave={() => setHoveredLogId(null)} sx={{ cursor: 'pointer', '&:hover': { backgroundColor: 'rgba(0, 255, 0, 0.1)', } }} > {formatDate(log.execution_date)} {getLogStatusChip(log.status)} {formatLogResult(log.result_data)} {log.execution_time_ms ? `${log.execution_time_ms}ms` : 'N/A'} {hoveredLogId === log.id && ( Full Details: {log.error_message && ( Error: {log.error_message} )} {log.result_data && ( Result Data: {typeof log.result_data === 'string' ? log.result_data : JSON.stringify(log.result_data, null, 2)} )} )} ))}
{logs.logs.length >= 10 && ( Showing latest 10 logs. View all logs in OAuth Monitoring section. )}
) : ( No monitoring logs available yet. Logs will appear after the first scheduled check. )} )} {/* Existing connection status messages */} {!task && platformStatus?.connected && ( Connected but no monitoring task. Create one manually or wait for onboarding completion. )} {!platformStatus?.connected && ( Not connected. Connect in onboarding step 5. )} ); })}
); }; export default OAuthTokenStatus;