144 lines
3.3 KiB
TypeScript
144 lines
3.3 KiB
TypeScript
|
|
import React from 'react';
|
||
|
|
import { View, Text, TouchableOpacity, StyleSheet, FlatList } from 'react-native';
|
||
|
|
import { COLORS, FONTS } from '../constants/theme';
|
||
|
|
import { Transaction } from '../types';
|
||
|
|
import { formatRupiah, formatDate } from '../utils/helpers';
|
||
|
|
|
||
|
|
interface TransactionListProps {
|
||
|
|
transactions: Transaction[];
|
||
|
|
onDelete: (id: string) => void;
|
||
|
|
}
|
||
|
|
|
||
|
|
export const TransactionList: React.FC<TransactionListProps> = ({
|
||
|
|
transactions,
|
||
|
|
onDelete,
|
||
|
|
}) => {
|
||
|
|
const renderItem = ({ item }: { item: Transaction }) => {
|
||
|
|
const isExpense = item.type === 'expense';
|
||
|
|
|
||
|
|
return (
|
||
|
|
<View style={styles.item}>
|
||
|
|
<View style={styles.info}>
|
||
|
|
<Text style={styles.description}>{item.description}</Text>
|
||
|
|
<Text style={styles.category}>{item.category}</Text>
|
||
|
|
<Text style={styles.date}>{formatDate(item.date)}</Text>
|
||
|
|
</View>
|
||
|
|
<View style={styles.amountContainer}>
|
||
|
|
<Text style={[styles.amount, isExpense ? styles.expense : styles.income]}>
|
||
|
|
{isExpense ? '-' : '+'}{formatRupiah(item.amount)}
|
||
|
|
</Text>
|
||
|
|
<TouchableOpacity
|
||
|
|
style={styles.deleteButton}
|
||
|
|
onPress={() => onDelete(item.id)}
|
||
|
|
>
|
||
|
|
<Text style={styles.deleteText}>Hapus</Text>
|
||
|
|
</TouchableOpacity>
|
||
|
|
</View>
|
||
|
|
</View>
|
||
|
|
);
|
||
|
|
};
|
||
|
|
|
||
|
|
if (transactions.length === 0) {
|
||
|
|
return (
|
||
|
|
<View style={styles.empty}>
|
||
|
|
<Text style={styles.emptyText}>Belum ada transaksi</Text>
|
||
|
|
<Text style={styles.emptySubtext}>
|
||
|
|
Tambahkan transaksi pertama Anda
|
||
|
|
</Text>
|
||
|
|
</View>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
return (
|
||
|
|
<View style={styles.container}>
|
||
|
|
<Text style={styles.title}>Riwayat Transaksi</Text>
|
||
|
|
<FlatList
|
||
|
|
data={transactions}
|
||
|
|
keyExtractor={(item) => item.id}
|
||
|
|
renderItem={renderItem}
|
||
|
|
scrollEnabled={false}
|
||
|
|
/>
|
||
|
|
</View>
|
||
|
|
);
|
||
|
|
};
|
||
|
|
|
||
|
|
const styles = StyleSheet.create({
|
||
|
|
container: {
|
||
|
|
margin: 16,
|
||
|
|
marginTop: 0,
|
||
|
|
},
|
||
|
|
title: {
|
||
|
|
fontSize: FONTS.regular,
|
||
|
|
fontWeight: 'bold',
|
||
|
|
color: COLORS.text,
|
||
|
|
marginBottom: 12,
|
||
|
|
},
|
||
|
|
item: {
|
||
|
|
backgroundColor: COLORS.card,
|
||
|
|
padding: 16,
|
||
|
|
borderRadius: 12,
|
||
|
|
marginBottom: 8,
|
||
|
|
flexDirection: 'row',
|
||
|
|
justifyContent: 'space-between',
|
||
|
|
alignItems: 'center',
|
||
|
|
shadowColor: '#000',
|
||
|
|
shadowOffset: { width: 0, height: 1 },
|
||
|
|
shadowOpacity: 0.05,
|
||
|
|
shadowRadius: 2,
|
||
|
|
elevation: 1,
|
||
|
|
},
|
||
|
|
info: {
|
||
|
|
flex: 1,
|
||
|
|
},
|
||
|
|
description: {
|
||
|
|
fontSize: FONTS.regular,
|
||
|
|
fontWeight: '600',
|
||
|
|
color: COLORS.text,
|
||
|
|
},
|
||
|
|
category: {
|
||
|
|
fontSize: FONTS.small,
|
||
|
|
color: COLORS.textSecondary,
|
||
|
|
marginTop: 2,
|
||
|
|
},
|
||
|
|
date: {
|
||
|
|
fontSize: FONTS.small,
|
||
|
|
color: COLORS.textSecondary,
|
||
|
|
marginTop: 2,
|
||
|
|
},
|
||
|
|
amountContainer: {
|
||
|
|
alignItems: 'flex-end',
|
||
|
|
},
|
||
|
|
amount: {
|
||
|
|
fontSize: FONTS.regular,
|
||
|
|
fontWeight: 'bold',
|
||
|
|
},
|
||
|
|
income: {
|
||
|
|
color: COLORS.income,
|
||
|
|
},
|
||
|
|
expense: {
|
||
|
|
color: COLORS.expense,
|
||
|
|
},
|
||
|
|
deleteButton: {
|
||
|
|
marginTop: 4,
|
||
|
|
paddingHorizontal: 8,
|
||
|
|
paddingVertical: 4,
|
||
|
|
},
|
||
|
|
deleteText: {
|
||
|
|
fontSize: FONTS.small,
|
||
|
|
color: COLORS.expense,
|
||
|
|
},
|
||
|
|
empty: {
|
||
|
|
margin: 16,
|
||
|
|
padding: 40,
|
||
|
|
alignItems: 'center',
|
||
|
|
},
|
||
|
|
emptyText: {
|
||
|
|
fontSize: FONTS.regular,
|
||
|
|
color: COLORS.textSecondary,
|
||
|
|
},
|
||
|
|
emptySubtext: {
|
||
|
|
fontSize: FONTS.small,
|
||
|
|
color: COLORS.textSecondary,
|
||
|
|
marginTop: 8,
|
||
|
|
},
|
||
|
|
});
|