expo-popcore-old/components/HistoryCard.tsx

201 lines
4.7 KiB
TypeScript

import React from 'react';
import {
View,
Text,
StyleSheet,
TouchableOpacity,
StyleProp,
ViewStyle,
} from 'react-native';
import { ThemedText } from '@/components/themed-text';
import { ThemedView } from '@/components/themed-view';
import { Colors, Spacing, BorderRadius, FontSize, Shadow } from '@/constants/theme';
type HistoryStatus = 'running' | 'completed' | 'failed';
interface HistoryCardProps {
templateName: string;
createdAt: string;
preview?: string;
status: HistoryStatus;
onView?: () => void;
onDelete?: () => void;
style?: StyleProp<ViewStyle>;
}
export function HistoryCard({
templateName,
createdAt,
preview,
status,
onView,
onDelete,
style,
}: HistoryCardProps) {
const formatTimeAgo = (dateString: string) => {
const now = new Date();
const past = new Date(dateString);
const diffInMinutes = Math.floor((now.getTime() - past.getTime()) / 60000);
if (diffInMinutes < 1) return 'Just now';
if (diffInMinutes < 60) return `${diffInMinutes} minutes ago`;
const diffInHours = Math.floor(diffInMinutes / 60);
if (diffInHours < 24) return `${diffInHours} hours ago`;
const diffInDays = Math.floor(diffInHours / 24);
if (diffInDays < 30) return `${diffInDays} days ago`;
return past.toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
});
};
const getStatusConfig = (status: HistoryStatus) => {
switch (status) {
case 'running':
return {
color: Colors.brand.secondary,
text: '进行中',
bgColor: '#E8F8F7',
};
case 'completed':
return {
color: Colors.brand.success,
text: '已完成',
bgColor: '#E8F5E9',
};
case 'failed':
return {
color: Colors.brand.danger,
text: '失败',
bgColor: '#FFEBEB',
};
}
};
const statusConfig = getStatusConfig(status);
return (
<ThemedView style={[styles.container, style]}>
<View style={styles.header}>
<Text style={styles.templateName}>{templateName}</Text>
<Text style={styles.timeText}>{formatTimeAgo(createdAt)}</Text>
</View>
<View style={styles.content}>
{preview ? (
<Text style={styles.previewText} numberOfLines={2}>
{preview}
</Text>
) : (
<View
style={[
styles.statusBadge,
{ backgroundColor: statusConfig.bgColor },
]}
>
<Text style={[styles.statusText, { color: statusConfig.color }]}>
{statusConfig.text}
</Text>
</View>
)}
</View>
<View style={styles.actions}>
<TouchableOpacity
style={styles.viewButton}
onPress={onView}
activeOpacity={0.7}
>
<Text style={styles.viewButtonText}></Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.deleteButton}
onPress={onDelete}
activeOpacity={0.7}
>
<Text style={styles.deleteButtonText}></Text>
</TouchableOpacity>
</View>
</ThemedView>
);
}
const styles = StyleSheet.create({
container: {
backgroundColor: Colors.background.secondary,
borderRadius: BorderRadius.md,
padding: Spacing.md,
marginVertical: Spacing.sm,
marginHorizontal: Spacing.md,
...Shadow.small,
},
header: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'flex-start',
marginBottom: Spacing.md,
},
templateName: {
fontSize: FontSize.md,
fontWeight: '600',
color: Colors.text.primary,
flex: 1,
marginRight: Spacing.sm,
},
timeText: {
fontSize: FontSize.xs,
color: Colors.text.tertiary,
textAlign: 'right',
},
content: {
marginBottom: Spacing.md,
},
previewText: {
fontSize: FontSize.sm,
color: Colors.text.secondary,
lineHeight: 20,
},
statusBadge: {
paddingHorizontal: Spacing.md,
paddingVertical: 6,
borderRadius: BorderRadius.full,
alignSelf: 'flex-start',
},
statusText: {
fontSize: FontSize.sm,
fontWeight: '500',
},
actions: {
flexDirection: 'row',
gap: Spacing.sm,
},
viewButton: {
flex: 1,
paddingVertical: 10,
paddingHorizontal: Spacing.md,
borderRadius: BorderRadius.sm,
borderWidth: 1,
borderColor: Colors.brand.primary,
alignItems: 'center',
},
viewButtonText: {
color: Colors.brand.primary,
fontSize: FontSize.sm,
fontWeight: '500',
},
deleteButton: {
paddingVertical: 10,
paddingHorizontal: Spacing.md,
alignItems: 'center',
},
deleteButtonText: {
color: Colors.brand.danger,
fontSize: FontSize.sm,
fontWeight: '500',
},
});