import React, { useState, useEffect } from 'react'; import { Table, Button, Stack, Title, Modal, Select, TextInput, Pagination, Group, ActionIcon, Badge, Card, Text, Loader, Alert, Code, ScrollArea, Flex, } from '@mantine/core'; import { IconRefresh, IconEye, IconX, IconSearch, IconClock, } from '@tabler/icons-react'; import { executionApi, functionApi } from '../services/apiService'; import { FunctionExecution, FunctionDefinition } from '../types'; import { notifications } from '@mantine/notifications'; const ExecutionList: React.FC = () => { const [executions, setExecutions] = useState([]); const [functions, setFunctions] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [selectedFunction, setSelectedFunction] = useState(''); const [searchTerm, setSearchTerm] = useState(''); const [page, setPage] = useState(1); const [totalPages, setTotalPages] = useState(1); const [selectedExecution, setSelectedExecution] = useState(null); const [executionLogs, setExecutionLogs] = useState([]); const [logsModalOpened, setLogsModalOpened] = useState(false); const [logsLoading, setLogsLoading] = useState(false); const limit = 20; const loadExecutions = async () => { try { setLoading(true); setError(null); const offset = (page - 1) * limit; const functionId = selectedFunction || undefined; const response = await executionApi.list(functionId, limit, offset); setExecutions(response.data.executions || []); // Calculate total pages (rough estimate) const hasMore = response.data.executions?.length === limit; setTotalPages(hasMore ? page + 1 : page); } catch (err: any) { setError(err.response?.data?.error || 'Failed to load executions'); console.error('Error loading executions:', err); } finally { setLoading(false); } }; const loadFunctions = async () => { try { const response = await functionApi.list(); setFunctions(response.data.functions || []); } catch (err) { console.error('Error loading functions:', err); } }; useEffect(() => { loadFunctions(); }, []); useEffect(() => { loadExecutions(); }, [page, selectedFunction]); const handleRefresh = () => { loadExecutions(); }; const handleViewLogs = async (execution: FunctionExecution) => { setSelectedExecution(execution); setLogsModalOpened(true); setLogsLoading(true); try { const response = await executionApi.getLogs(execution.id); setExecutionLogs(response.data.logs || []); } catch (err: any) { notifications.show({ title: 'Error', message: err.response?.data?.error || 'Failed to load logs', color: 'red', }); setExecutionLogs([]); } finally { setLogsLoading(false); } }; const handleCancelExecution = async (executionId: string) => { try { await executionApi.cancel(executionId); notifications.show({ title: 'Success', message: 'Execution cancelled successfully', color: 'green', }); loadExecutions(); } catch (err: any) { notifications.show({ title: 'Error', message: err.response?.data?.error || 'Failed to cancel execution', color: 'red', }); } }; const getStatusColor = (status: FunctionExecution['status']) => { switch (status) { case 'completed': return 'green'; case 'failed': return 'red'; case 'running': return 'blue'; case 'pending': return 'yellow'; case 'timeout': return 'orange'; case 'canceled': return 'gray'; default: return 'gray'; } }; const formatDuration = (nanoseconds: number) => { if (!nanoseconds) return 'N/A'; const milliseconds = nanoseconds / 1000000; if (milliseconds < 1000) { return `${milliseconds.toFixed(0)}ms`; } return `${(milliseconds / 1000).toFixed(2)}s`; }; const formatMemory = (bytes: number) => { if (!bytes) return 'N/A'; if (bytes < 1024 * 1024) { return `${(bytes / 1024).toFixed(0)}KB`; } return `${(bytes / (1024 * 1024)).toFixed(1)}MB`; }; const formatDate = (dateString: string) => { return new Date(dateString).toLocaleString(); }; const getFunctionName = (functionId: string) => { const func = functions.find(f => f.id === functionId); return func?.name || 'Unknown Function'; }; const filteredExecutions = executions.filter(execution => { if (!searchTerm) return true; const functionName = getFunctionName(execution.function_id); return functionName.toLowerCase().includes(searchTerm.toLowerCase()) || execution.id.toLowerCase().includes(searchTerm.toLowerCase()) || execution.status.toLowerCase().includes(searchTerm.toLowerCase()); }); if (loading && executions.length === 0) { return ( Function Executions Loading executions... ); } return ( Function Executions setSearchTerm(event.currentTarget.value)} leftSection={} style={{ width: 300 }} />