Add transaction display feature to HomeScreen

This commit is contained in:
2025-09-10 20:34:38 +05:00
parent b925b48dd4
commit 4efaf2543e

View File

@@ -8,6 +8,7 @@ import {
TouchableOpacity, TouchableOpacity,
ActivityIndicator, ActivityIndicator,
RefreshControl, RefreshControl,
FlatList,
} from 'react-native'; } from 'react-native';
import { StatusBar } from 'expo-status-bar'; import { StatusBar } from 'expo-status-bar';
import { Ionicons } from '@expo/vector-icons'; import { Ionicons } from '@expo/vector-icons';
@@ -16,6 +17,89 @@ import { COLORS } from '../../constants/colors';
import MetricCard from '../../components/MetricCard'; import MetricCard from '../../components/MetricCard';
import apiService from '../../services/apiService'; import apiService from '../../services/apiService';
const STATIC_TRANSACTIONS = [
{
id: 1,
type: 'debit',
description: 'Canva Design and Publishing',
date: '2025-09-07T10:30:00Z',
amount: 10.0,
currency: 'EUR',
},
{
id: 2,
type: 'debit',
description: 'Google',
date: '2025-09-07T11:00:00Z',
amount: 12.99,
currency: 'SAR',
},
{
id: 3,
type: 'debit',
description: 'Canva Design and Publishing',
date: '2025-09-05T14:15:00Z',
amount: 10.0,
currency: 'EUR',
},
{
id: 4,
type: 'credit',
description: 'Töleg alyňýan hasap',
date: '2025-09-03T09:00:00Z',
amount: 20.0,
currency: 'USD',
},
{
id: 5,
type: 'debit',
description: 'Комиссия за оплату',
date: '2025-09-03T09:01:00Z',
amount: 0.12,
currency: 'USD',
},
{
id: 6,
type: 'credit',
description: 'Hasaby doldurmak',
date: '2025-09-02T18:45:00Z',
amount: 10.0,
currency: 'USD',
},
{
id: 7,
type: 'debit',
description: 'Canva Design and Publishing',
date: '2025-09-01T12:00:00Z',
amount: 10.0,
currency: 'EUR',
},
{
id: 8,
type: 'debit',
description: 'Netflix',
date: '2025-08-28T16:00:00Z',
amount: 9.99,
currency: 'USD',
},
{
id: 9,
type: 'credit',
description: 'Bank Transfer',
date: '2025-08-25T08:20:00Z',
amount: 500.0,
currency: 'TMT',
},
{
id: 10,
type: 'debit',
description: 'Amazon',
date: '2025-08-23T19:55:00Z',
amount: 45.5,
currency: 'USD',
},
];
const HomeScreen = () => { const HomeScreen = () => {
const { user, logout } = useAuth(); const { user, logout } = useAuth();
const [metrics, setMetrics] = useState(null); const [metrics, setMetrics] = useState(null);
@@ -25,6 +109,7 @@ const HomeScreen = () => {
const [cardBalanceError, setCardBalanceError] = useState(null); const [cardBalanceError, setCardBalanceError] = useState(null);
const [isBalanceVisible, setIsBalanceVisible] = useState(true); const [isBalanceVisible, setIsBalanceVisible] = useState(true);
const [refreshing, setRefreshing] = useState(false); const [refreshing, setRefreshing] = useState(false);
const [transactions, setTransactions] = useState(STATIC_TRANSACTIONS);
const showBalanceCard = !loadingCardBalance && cardBalanceError === null && cardBalance !== null; const showBalanceCard = !loadingCardBalance && cardBalanceError === null && cardBalance !== null;
@@ -50,16 +135,18 @@ const HomeScreen = () => {
}, []); }, []);
useEffect(() => { useEffect(() => {
const fetchCardBalance = async () => { const fetchCardData = async () => {
// Ensure user has filled all required card & passport fields // Ensure user has filled all required card & passport fields
if (!user?.passport_serie || !user?.passport_id || !user?.card_number || !user?.card_month || !user?.card_year) { if (!user?.passport_serie || !user?.passport_id || !user?.card_number || !user?.card_month || !user?.card_year) {
setLoadingCardBalance(false); setLoadingCardBalance(false);
return; return;
} }
setLoadingCardBalance(true);
try { try {
const res = await apiService.getCardBalanceQuickCheck(); const res = await apiService.getCardBalanceQuickCheck();
if (res.success) { if (res.success) {
// Try common balance keys else fallback to raw
const raw = res.data; const raw = res.data;
let balanceValue = null; let balanceValue = null;
if (raw && typeof raw === 'object') { if (raw && typeof raw === 'object') {
@@ -73,12 +160,13 @@ const HomeScreen = () => {
} }
} catch (e) { } catch (e) {
console.warn('Error fetching card balance:', e); console.warn('Error fetching card balance:', e);
setCardBalanceError('Error fetching balance');
} finally { } finally {
setLoadingCardBalance(false); setLoadingCardBalance(false);
} }
}; };
fetchCardBalance(); fetchCardData();
}, [user]); }, [user]);
const handleRefresh = async () => { const handleRefresh = async () => {
@@ -124,6 +212,57 @@ const HomeScreen = () => {
setRefreshing(false); setRefreshing(false);
}; };
const groupedTransactions = transactions.reduce((acc, transaction) => {
const date = new Date(transaction.date).toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric',
});
if (!acc[date]) {
acc[date] = [];
}
acc[date].push(transaction);
return acc;
}, {});
const renderTransactionItem = ({ item }) => (
<View style={styles.transactionItem}>
<View style={styles.transactionIcon}>
<Ionicons
name={item.type === 'credit' ? 'arrow-down' : 'arrow-up'}
size={20}
color={item.type === 'credit' ? COLORS.success : COLORS.danger}
/>
</View>
<View style={styles.transactionDetails}>
<Text style={styles.transactionTitle}>{item.description}</Text>
<Text style={styles.transactionDate}>{new Date(item.date).toLocaleTimeString()}</Text>
</View>
<Text
style={[
styles.transactionAmount,
{ color: item.type === 'credit' ? COLORS.success : COLORS.textPrimary },
]}
>
{item.type === 'credit' ? '+' : '-'}
{item.amount} {item.currency}
</Text>
</View>
);
const renderTransactionSection = ({ item: date }) => (
<View>
<Text style={styles.transactionDateHeader}>{date}</Text>
<FlatList
data={groupedTransactions[date]}
renderItem={renderTransactionItem}
keyExtractor={(item) => item.id.toString()}
scrollEnabled={false}
ItemSeparatorComponent={() => <View style={styles.separator} />}
/>
</View>
);
return ( return (
<SafeAreaView style={styles.container}> <SafeAreaView style={styles.container}>
<StatusBar style="dark" /> <StatusBar style="dark" />
@@ -149,7 +288,7 @@ const HomeScreen = () => {
{/* Metrics */} {/* Metrics */}
<View style={styles.section}> <View style={styles.section}>
<Text style={styles.sectionTitle}>Metrics</Text> <Text style={styles.sectionTitle}>Görkezijiler</Text>
{loadingMetrics ? ( {loadingMetrics ? (
<ActivityIndicator color={COLORS.primary} style={{ marginTop: 16 }} /> <ActivityIndicator color={COLORS.primary} style={{ marginTop: 16 }} />
) : ( ) : (
@@ -181,6 +320,29 @@ const HomeScreen = () => {
</View> </View>
</View> </View>
)} )}
{/* Card Transactions */}
{showBalanceCard && (
<View style={styles.section}>
<View style={styles.sectionHeader}>
<Text style={styles.sectionTitle}>Transactions</Text>
<TouchableOpacity>
<Text style={styles.seeAllText}>See All</Text>
</TouchableOpacity>
</View>
{transactions.length > 0 ? (
<FlatList
data={Object.keys(groupedTransactions)}
renderItem={renderTransactionSection}
keyExtractor={(date) => date}
scrollEnabled={false}
ItemSeparatorComponent={() => <View style={styles.separator} />}
/>
) : (
<Text style={{ textAlign: 'center', color: COLORS.textSecondary }}>No transactions yet.</Text>
)}
</View>
)}
</ScrollView> </ScrollView>
</SafeAreaView> </SafeAreaView>
); );
@@ -311,20 +473,21 @@ const styles = StyleSheet.create({
textAlign: 'center', textAlign: 'center',
}, },
transactionsList: { transactionsList: {
gap: 16, marginTop: 16,
}, },
transactionItem: { transactionItem: {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
paddingVertical: 12,
}, },
transactionIcon: { transactionIcon: {
width: 40, width: 44,
height: 40, height: 44,
borderRadius: 20, borderRadius: 22,
backgroundColor: COLORS.backgroundSecondary, backgroundColor: COLORS.background,
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
marginRight: 12, marginRight: 16,
}, },
transactionDetails: { transactionDetails: {
flex: 1, flex: 1,
@@ -333,34 +496,29 @@ const styles = StyleSheet.create({
fontSize: 16, fontSize: 16,
fontWeight: '600', fontWeight: '600',
color: COLORS.textPrimary, color: COLORS.textPrimary,
marginBottom: 2,
}, },
transactionDate: { transactionDate: {
fontSize: 14, fontSize: 13,
color: COLORS.textSecondary, color: COLORS.textSecondary,
}, },
transactionAmount: { transactionAmount: {
fontSize: 16, fontSize: 16,
fontWeight: 'bold', fontWeight: '700',
color: COLORS.success,
}, },
servicesGrid: { transactionDateHeader: {
flexDirection: 'row', fontSize: 14,
flexWrap: 'wrap', fontWeight: '600',
justifyContent: 'space-between',
},
serviceItem: {
width: '48%',
padding: 16,
backgroundColor: COLORS.backgroundSecondary,
borderRadius: 12,
alignItems: 'center',
marginBottom: 12,
},
serviceText: {
fontSize: 12,
color: COLORS.textSecondary, color: COLORS.textSecondary,
textAlign: 'center', backgroundColor: COLORS.white,
marginTop: 8, paddingTop: 16,
paddingBottom: 8,
paddingHorizontal: 4,
},
separator: {
height: 1,
backgroundColor: COLORS.gray[200],
marginLeft: 60, // Align with transaction details, skipping the icon
}, },
metricsGrid: { metricsGrid: {
flexDirection: 'row', flexDirection: 'row',