Files
CBS-Mobile-App/src/components/TransactionItem/index.tsx
T

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);