110 lines
3.6 KiB
TypeScript
110 lines
3.6 KiB
TypeScript
import React, { memo } from 'react';
|
|
import { StyleSheet, Text, View } from 'react-native';
|
|
import Ionicons from '@react-native-vector-icons/ionicons';
|
|
|
|
import { useTheme } from '../../../theme';
|
|
import { formatDate } from '../../../utils/formatters';
|
|
import { Card } from '../../../components';
|
|
|
|
interface TimelineStep {
|
|
stage: string;
|
|
timestamp: string;
|
|
active: boolean;
|
|
}
|
|
|
|
interface LoanTimelineCardProps {
|
|
title: string;
|
|
subtitle?: string;
|
|
timeline: TimelineStep[];
|
|
}
|
|
|
|
const LoanTimelineCardComponent = ({ title, subtitle, timeline }: LoanTimelineCardProps) => {
|
|
const theme = useTheme();
|
|
|
|
return (
|
|
<Card style={styles.card}>
|
|
<View style={styles.header}>
|
|
<Text style={[styles.title, { color: theme.colors.textPrimary }]}>{title}</Text>
|
|
{subtitle && (
|
|
<Text style={[styles.subtitle, { color: theme.colors.textSecondary }]}>{subtitle}</Text>
|
|
)}
|
|
</View>
|
|
<View style={styles.timeline}>
|
|
{timeline.map((step, index) => {
|
|
const isLast = index === timeline.length - 1;
|
|
const isCompleted = step.active && step.timestamp !== '';
|
|
const isCurrent = step.active && step.timestamp === '' && index > 0
|
|
? false
|
|
: step.active && !timeline[index + 1]?.active;
|
|
const isPending = !step.active;
|
|
|
|
let dotColor = theme.colors.textMuted;
|
|
let iconName = 'ellipse-outline';
|
|
if (isCompleted) {
|
|
dotColor = theme.colors.success;
|
|
iconName = 'checkmark-circle';
|
|
} else if (isCurrent) {
|
|
dotColor = theme.colors.primary;
|
|
iconName = 'radio-button-on';
|
|
}
|
|
|
|
return (
|
|
<View key={index} style={styles.stepRow}>
|
|
<View style={styles.stepIndicator}>
|
|
<Ionicons name={iconName as any} size={20} color={dotColor} />
|
|
{!isLast && (
|
|
<View
|
|
style={[
|
|
styles.connector,
|
|
{
|
|
backgroundColor: isCompleted ? theme.colors.success : theme.colors.cardBorder,
|
|
},
|
|
]}
|
|
/>
|
|
)}
|
|
</View>
|
|
<View style={[styles.stepContent, !isLast && styles.stepContentSpaced]}>
|
|
<Text
|
|
style={[
|
|
styles.stepLabel,
|
|
{
|
|
color: isPending ? theme.colors.textMuted : theme.colors.textPrimary,
|
|
fontWeight: isCurrent ? '800' : '600',
|
|
},
|
|
]}
|
|
>
|
|
{step.stage}
|
|
</Text>
|
|
{step.timestamp ? (
|
|
<Text style={[styles.stepTime, { color: theme.colors.textMuted }]}>
|
|
{formatDate(step.timestamp)}
|
|
</Text>
|
|
) : (
|
|
<Text style={[styles.stepTime, { color: theme.colors.textMuted }]}>Pending</Text>
|
|
)}
|
|
</View>
|
|
</View>
|
|
);
|
|
})}
|
|
</View>
|
|
</Card>
|
|
);
|
|
};
|
|
|
|
const styles = StyleSheet.create({
|
|
card: { gap: 14 },
|
|
header: { gap: 4 },
|
|
title: { fontSize: 16, fontWeight: '800' },
|
|
subtitle: { fontSize: 13, lineHeight: 18 },
|
|
timeline: { gap: 0 },
|
|
stepRow: { flexDirection: 'row', gap: 12 },
|
|
stepIndicator: { alignItems: 'center', width: 24 },
|
|
connector: { width: 2, flex: 1, minHeight: 20 },
|
|
stepContent: { flex: 1, paddingTop: 1 },
|
|
stepContentSpaced: { paddingBottom: 16 },
|
|
stepLabel: { fontSize: 14 },
|
|
stepTime: { fontSize: 12, marginTop: 2 },
|
|
});
|
|
|
|
export const LoanTimelineCard = memo(LoanTimelineCardComponent);
|