- 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)
161 lines
3.8 KiB
TypeScript
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'
|
|
}
|
|
});
|