import React from 'react'; import { View, Text, StyleSheet, TouchableOpacity, useColorScheme, } from 'react-native'; import { Ionicons } from '@expo/vector-icons'; import { Colors } from '../constants/Colors'; import { TaskStatusBadge } from './TaskStatusBadge'; import type { TaskSummary } from '../lib/api'; interface TaskListItemProps { task: TaskSummary; onPress: (task: TaskSummary) => void; } /** * Format relative time from a date string */ function formatRelativeTime(dateString: string): string { const date = new Date(dateString); const now = new Date(); const diffMs = now.getTime() - date.getTime(); const diffSec = Math.floor(diffMs / 1000); const diffMin = Math.floor(diffSec / 60); const diffHour = Math.floor(diffMin / 60); const diffDay = Math.floor(diffHour / 24); if (diffSec < 60) return 'just now'; if (diffMin < 60) return `${diffMin}m ago`; if (diffHour < 24) return `${diffHour}h ago`; if (diffDay < 7) return `${diffDay}d ago`; return date.toLocaleDateString(); } /** * Truncate text with ellipsis */ function truncate(text: string, maxLength: number): string { if (text.length <= maxLength) return text; return text.slice(0, maxLength - 3) + '...'; } export function TaskListItem({ task, onPress }: TaskListItemProps) { const colorScheme = useColorScheme() ?? 'light'; const colors = Colors[colorScheme]; const isRunning = ['running', 'initializing', 'starting'].includes(task.status); const isCompleted = ['done', 'merged'].includes(task.status); const isFailed = task.status === 'failed'; return ( onPress(task)} activeOpacity={0.7} > {task.name} {task.isSupervisor && ( )} {task.contractName && ( {task.contractName} )} {task.progressSummary && ( {truncate(task.progressSummary, 100)} )} {isRunning ? `Started ${formatRelativeTime(task.updatedAt)}` : isCompleted ? `Completed ${formatRelativeTime(task.updatedAt)}` : isFailed ? `Failed ${formatRelativeTime(task.updatedAt)}` : `Created ${formatRelativeTime(task.createdAt)}`} {task.subtaskCount > 0 && ( {task.subtaskCount} )} ); } const styles = StyleSheet.create({ container: { flexDirection: 'row', alignItems: 'center', paddingVertical: 12, paddingHorizontal: 16, borderBottomWidth: StyleSheet.hairlineWidth, minHeight: 64, }, leftSection: { marginRight: 12, alignItems: 'center', justifyContent: 'center', }, content: { flex: 1, gap: 2, }, header: { flexDirection: 'row', alignItems: 'center', gap: 6, }, name: { fontSize: 16, fontWeight: '600', flex: 1, }, supervisorBadge: { padding: 2, }, contractName: { fontSize: 13, }, progressSummary: { fontSize: 13, marginTop: 2, }, footer: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', marginTop: 4, }, time: { fontSize: 12, }, subtaskBadge: { flexDirection: 'row', alignItems: 'center', gap: 4, }, subtaskCount: { fontSize: 12, }, rightSection: { marginLeft: 8, }, });