171 lines
4.3 KiB
TypeScript
171 lines
4.3 KiB
TypeScript
import React, { memo, useMemo } from 'react';
|
|
import {
|
|
StyleSheet,
|
|
Text,
|
|
View,
|
|
} from 'react-native';
|
|
import Ionicons from '@react-native-vector-icons/ionicons';
|
|
|
|
import type { TransactionRecord } from '../../types/app';
|
|
import { useTheme } from '../../theme';
|
|
import { formatCurrency, formatDate } from '../../utils/formatters';
|
|
|
|
interface TransactionItemProps {
|
|
item: TransactionRecord;
|
|
}
|
|
|
|
type RiskLevel = 'safe' | 'medium' | 'high';
|
|
|
|
const TransactionItemComponent = ({ item }: TransactionItemProps) => {
|
|
const theme = useTheme();
|
|
const isCredit = item.direction === 'credit';
|
|
|
|
const riskLevel = useMemo<RiskLevel>(() => {
|
|
const sum = item.id.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0);
|
|
if (sum % 10 < 7) return 'safe';
|
|
if (sum % 10 < 9) return 'medium';
|
|
return 'high';
|
|
}, [item.id]);
|
|
|
|
const riskConfig = {
|
|
safe: { color: theme.colors.success, label: 'SECURE', icon: 'shield-checkmark' as const },
|
|
medium: { color: theme.colors.warning, label: 'FLAGGED', icon: 'warning' as const },
|
|
high: { color: theme.colors.danger, label: 'HIGH RISK', icon: 'alert-circle' as const },
|
|
}[riskLevel];
|
|
|
|
return (
|
|
<View
|
|
style={[
|
|
styles.wrapper,
|
|
{
|
|
borderBottomColor: 'rgba(255, 255, 255, 0.05)',
|
|
},
|
|
]}
|
|
>
|
|
<View style={styles.leading}>
|
|
<View
|
|
style={[
|
|
styles.icon,
|
|
{
|
|
backgroundColor: isCredit
|
|
? `${theme.colors.success}10`
|
|
: 'rgba(255, 255, 255, 0.05)',
|
|
borderColor: isCredit ? `${theme.colors.success}30` : 'rgba(255, 255, 255, 0.1)',
|
|
},
|
|
]}
|
|
>
|
|
<Ionicons
|
|
name={isCredit ? 'chevron-down' : 'chevron-up'}
|
|
size={18}
|
|
color={isCredit ? theme.colors.success : theme.colors.white}
|
|
/>
|
|
</View>
|
|
<View style={styles.copy}>
|
|
<View style={styles.titleRow}>
|
|
<Text style={[styles.title, { color: theme.colors.white }]}>{item.title}</Text>
|
|
</View>
|
|
<View style={styles.subtitleRow}>
|
|
<Text style={[styles.subtitle, { color: 'rgba(255, 255, 255, 0.5)' }]}>
|
|
{item.counterparty}
|
|
</Text>
|
|
<View style={styles.dot} />
|
|
<Text style={[styles.subtitle, { color: 'rgba(255, 255, 255, 0.5)' }]}>
|
|
{formatDate(item.occurredAt)}
|
|
</Text>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
|
|
<View style={styles.trailing}>
|
|
<Text
|
|
style={[
|
|
styles.amount,
|
|
{ color: isCredit ? theme.colors.success : theme.colors.white },
|
|
]}
|
|
>
|
|
{isCredit ? '+' : '-'}
|
|
{formatCurrency(item.amount)}
|
|
</Text>
|
|
<View style={[styles.riskBadge, { backgroundColor: `${riskConfig.color}10`, borderColor: `${riskConfig.color}20` }]}>
|
|
<Text style={[styles.riskText, { color: riskConfig.color }]}>{riskConfig.label}</Text>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
);
|
|
};
|
|
|
|
const styles = StyleSheet.create({
|
|
wrapper: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
justifyContent: 'space-between',
|
|
paddingVertical: 18,
|
|
borderBottomWidth: 1,
|
|
},
|
|
leading: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
gap: 16,
|
|
flex: 1,
|
|
},
|
|
icon: {
|
|
width: 46,
|
|
height: 46,
|
|
borderRadius: 23,
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
borderWidth: 1,
|
|
},
|
|
copy: {
|
|
gap: 4,
|
|
flex: 1,
|
|
},
|
|
titleRow: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
},
|
|
title: {
|
|
fontSize: 16,
|
|
fontWeight: '800',
|
|
letterSpacing: -0.3,
|
|
},
|
|
subtitleRow: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
gap: 6,
|
|
},
|
|
dot: {
|
|
width: 3,
|
|
height: 3,
|
|
borderRadius: 1.5,
|
|
backgroundColor: 'rgba(255, 255, 255, 0.2)',
|
|
},
|
|
subtitle: {
|
|
fontSize: 12,
|
|
fontWeight: '500',
|
|
},
|
|
trailing: {
|
|
alignItems: 'flex-end',
|
|
gap: 4,
|
|
},
|
|
amount: {
|
|
fontSize: 16,
|
|
fontWeight: '800',
|
|
letterSpacing: -0.2,
|
|
},
|
|
riskBadge: {
|
|
paddingHorizontal: 6,
|
|
paddingVertical: 2,
|
|
borderRadius: 6,
|
|
borderWidth: 1,
|
|
},
|
|
riskText: {
|
|
fontSize: 8,
|
|
fontWeight: '900',
|
|
letterSpacing: 0.5,
|
|
},
|
|
});
|
|
|
|
|
|
export const TransactionItem = memo(TransactionItemComponent);
|