Files
WalletTracker/src/components/SubscriptionCard.tsx
Arthur Lempereur 8bde3d4f21 Initial commit: WalletTracker app with Firebase integration
- Setup Expo project with TypeScript
- Implement authentication (Login/Signup/Logout)
- Create Dashboard, Transactions, Subscriptions, and Analysis screens
- Add Firebase services (Auth, Firestore, Storage)
- Implement real-time synchronization
- Add charts and analytics
- Create reusable components (Button, InputText, TransactionCard, SubscriptionCard)
- Configure React Navigation with bottom tabs
- Add Firestore security rules
- Create comprehensive documentation (README, FIREBASE_SETUP, TESTING)
2025-10-23 14:36:36 +02:00

161 lines
3.8 KiB
TypeScript

import React from 'react';
import { View, Text, StyleSheet, TouchableOpacity } from 'react-native';
import { Subscription } from '../types';
interface SubscriptionCardProps {
subscription: Subscription;
onPress?: () => void;
categoryIcon?: string;
categoryColor?: string;
}
export const SubscriptionCard: React.FC<SubscriptionCardProps> = ({
subscription,
onPress,
categoryIcon = '📱',
categoryColor = '#F8B739'
}) => {
const formatDate = (date: Date) => {
return new Intl.DateTimeFormat('fr-FR', {
day: '2-digit',
month: 'short'
}).format(date);
};
const getFrequencyLabel = (frequency: string) => {
switch (frequency) {
case 'daily':
return 'Quotidien';
case 'weekly':
return 'Hebdomadaire';
case 'monthly':
return 'Mensuel';
case 'yearly':
return 'Annuel';
default:
return frequency;
}
};
const getDaysUntilPayment = () => {
const today = new Date();
const daysUntil = Math.ceil(
(subscription.nextPaymentDate.getTime() - today.getTime()) / (1000 * 60 * 60 * 24)
);
if (daysUntil < 0) return 'En retard';
if (daysUntil === 0) return 'Aujourd\'hui';
if (daysUntil === 1) return 'Demain';
return `Dans ${daysUntil} jours`;
};
const isUpcoming = () => {
const today = new Date();
const daysUntil = Math.ceil(
(subscription.nextPaymentDate.getTime() - today.getTime()) / (1000 * 60 * 60 * 24)
);
return daysUntil <= subscription.reminderDaysBefore && daysUntil >= 0;
};
return (
<TouchableOpacity
style={[styles.card, isUpcoming() && styles.upcomingCard]}
onPress={onPress}
activeOpacity={0.7}
disabled={!onPress}
>
<View style={styles.leftSection}>
<View style={[styles.iconContainer, { backgroundColor: categoryColor + '20' }]}>
<Text style={styles.icon}>{categoryIcon}</Text>
</View>
<View style={styles.infoContainer}>
<Text style={styles.name}>{subscription.name}</Text>
<Text style={styles.frequency}>{getFrequencyLabel(subscription.frequency)}</Text>
<Text style={[styles.nextPayment, isUpcoming() && styles.upcomingText]}>
{getDaysUntilPayment()} {formatDate(subscription.nextPaymentDate)}
</Text>
</View>
</View>
<View style={styles.rightSection}>
<Text style={styles.amount}>{subscription.amount.toFixed(2)} </Text>
{!subscription.isActive && (
<Text style={styles.inactiveLabel}>Inactif</Text>
)}
</View>
</TouchableOpacity>
);
};
const styles = StyleSheet.create({
card: {
backgroundColor: '#FFF',
borderRadius: 12,
padding: 16,
marginBottom: 12,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3
},
upcomingCard: {
borderWidth: 2,
borderColor: '#FFA07A'
},
leftSection: {
flexDirection: 'row',
alignItems: 'center',
flex: 1
},
iconContainer: {
width: 48,
height: 48,
borderRadius: 24,
justifyContent: 'center',
alignItems: 'center',
marginRight: 12
},
icon: {
fontSize: 24
},
infoContainer: {
flex: 1
},
name: {
fontSize: 16,
fontWeight: '600',
color: '#333',
marginBottom: 4
},
frequency: {
fontSize: 13,
color: '#999',
marginBottom: 4
},
nextPayment: {
fontSize: 12,
color: '#666'
},
upcomingText: {
color: '#FF6B6B',
fontWeight: '600'
},
rightSection: {
alignItems: 'flex-end'
},
amount: {
fontSize: 18,
fontWeight: '700',
color: '#333'
},
inactiveLabel: {
fontSize: 11,
color: '#999',
marginTop: 4,
fontStyle: 'italic'
}
});