Fix: Resolve UI/UX issues and improve user experience

- Configure Firebase Auth with AsyncStorage persistence
- Fix 'Text strings must be rendered within <Text>' error in navigation
- Improve bottom tab bar: iOS style with blur effect, better height, rounded corners
- Fix Dashboard quick action buttons to open transaction modal directly
- Add auto-open modal when navigating from Dashboard
- Improve selection visibility in modals (type selector and categories)
- Add amount validation: only positive numbers, max 2 decimals
- Add padding to Dashboard content to avoid tab bar overlap
- Apply same fixes to both Transaction and Subscription screens
This commit is contained in:
2025-10-23 15:21:48 +02:00
parent fc1274b59d
commit 0db3832282
5 changed files with 73 additions and 21 deletions

View File

@@ -20,6 +20,7 @@ const firebaseConfig = {
const app = initializeApp(firebaseConfig); const app = initializeApp(firebaseConfig);
// Services Firebase // Services Firebase
// Note: AsyncStorage est géré automatiquement par Firebase pour React Native
export const auth = getAuth(app); export const auth = getAuth(app);
export const db = getFirestore(app); export const db = getFirestore(app);
export const storage = getStorage(app); export const storage = getStorage(app);

View File

@@ -2,7 +2,7 @@ import React from 'react';
import { NavigationContainer } from '@react-navigation/native'; import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack'; import { createStackNavigator } from '@react-navigation/stack';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { ActivityIndicator, View, StyleSheet } from 'react-native'; import { ActivityIndicator, View, StyleSheet, Text } from 'react-native';
import { useAuth } from '../hooks/useAuth'; import { useAuth } from '../hooks/useAuth';
import { LoginScreen } from '../screens/LoginScreen'; import { LoginScreen } from '../screens/LoginScreen';
@@ -25,16 +25,24 @@ const MainTabs = () => {
tabBarActiveTintColor: '#4A90E2', tabBarActiveTintColor: '#4A90E2',
tabBarInactiveTintColor: '#999', tabBarInactiveTintColor: '#999',
tabBarStyle: { tabBarStyle: {
backgroundColor: '#FFF', position: 'absolute',
borderTopWidth: 1, backgroundColor: 'rgba(255, 255, 255, 0.95)',
borderTopColor: '#E0E0E0', borderTopWidth: 0,
paddingBottom: 8, elevation: 0,
paddingTop: 8, shadowColor: '#000',
height: 60 shadowOffset: { width: 0, height: -2 },
shadowOpacity: 0.1,
shadowRadius: 8,
paddingBottom: 34, // Espace pour la barre iOS
paddingTop: 12,
height: 90, // Plus haute
borderTopLeftRadius: 20,
borderTopRightRadius: 20
}, },
tabBarLabelStyle: { tabBarLabelStyle: {
fontSize: 12, fontSize: 11,
fontWeight: '600' fontWeight: '600',
marginTop: 4
} }
}} }}
> >
@@ -75,9 +83,9 @@ const MainTabs = () => {
}; };
const TabIcon = ({ icon, color }: { icon: string; color: string }) => ( const TabIcon = ({ icon, color }: { icon: string; color: string }) => (
<View style={{ opacity: color === '#4A90E2' ? 1 : 0.5 }}> <Text style={{ fontSize: 24, opacity: color === '#4A90E2' ? 1 : 0.5 }}>
<View>{icon}</View> {icon}
</View> </Text>
); );
export const AppNavigator = () => { export const AppNavigator = () => {

View File

@@ -75,6 +75,7 @@ export const DashboardScreen = ({ navigation }: any) => {
return ( return (
<ScrollView <ScrollView
style={styles.container} style={styles.container}
contentContainerStyle={styles.scrollContent}
refreshControl={<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />} refreshControl={<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />}
> >
<View style={styles.header}> <View style={styles.header}>
@@ -140,14 +141,14 @@ export const DashboardScreen = ({ navigation }: any) => {
<View style={styles.quickActions}> <View style={styles.quickActions}>
<TouchableOpacity <TouchableOpacity
style={[styles.actionButton, styles.addExpenseButton]} style={[styles.actionButton, styles.addExpenseButton]}
onPress={() => navigation.navigate('Transactions', { type: 'expense' })} onPress={() => navigation.navigate('Transactions', { type: 'expense', openModal: true })}
> >
<Text style={styles.actionIcon}></Text> <Text style={styles.actionIcon}></Text>
<Text style={styles.actionText}>Dépense</Text> <Text style={styles.actionText}>Dépense</Text>
</TouchableOpacity> </TouchableOpacity>
<TouchableOpacity <TouchableOpacity
style={[styles.actionButton, styles.addIncomeButton]} style={[styles.actionButton, styles.addIncomeButton]}
onPress={() => navigation.navigate('Transactions', { type: 'income' })} onPress={() => navigation.navigate('Transactions', { type: 'income', openModal: true })}
> >
<Text style={styles.actionIcon}></Text> <Text style={styles.actionIcon}></Text>
<Text style={styles.actionText}>Revenu</Text> <Text style={styles.actionText}>Revenu</Text>
@@ -162,6 +163,9 @@ const styles = StyleSheet.create({
flex: 1, flex: 1,
backgroundColor: '#F8F9FA' backgroundColor: '#F8F9FA'
}, },
scrollContent: {
paddingBottom: 100 // Espace pour la tab bar
},
header: { header: {
flexDirection: 'row', flexDirection: 'row',
justifyContent: 'space-between', justifyContent: 'space-between',

View File

@@ -223,7 +223,16 @@ export const SubscriptionScreen = () => {
label="Montant (€)" label="Montant (€)"
placeholder="0.00" placeholder="0.00"
value={amount} value={amount}
onChangeText={setAmount} onChangeText={(text) => {
// Permettre uniquement les chiffres et un point décimal
const cleaned = text.replace(/[^0-9.]/g, '');
// Empêcher plusieurs points
const parts = cleaned.split('.');
if (parts.length > 2) return;
// Limiter à 2 décimales
if (parts[1] && parts[1].length > 2) return;
setAmount(cleaned);
}}
keyboardType="decimal-pad" keyboardType="decimal-pad"
/> />

View File

@@ -17,7 +17,7 @@ import { TransactionCard } from '../components/TransactionCard';
import { InputText } from '../components/InputText'; import { InputText } from '../components/InputText';
import { Button } from '../components/Button'; import { Button } from '../components/Button';
export const TransactionScreen = ({ route }: any) => { export const TransactionScreen = ({ route, navigation }: any) => {
const { user } = useAuth(); const { user } = useAuth();
const [transactions, setTransactions] = useState<Transaction[]>([]); const [transactions, setTransactions] = useState<Transaction[]>([]);
const [categories, setCategories] = useState<Category[]>([]); const [categories, setCategories] = useState<Category[]>([]);
@@ -48,6 +48,18 @@ export const TransactionScreen = ({ route }: any) => {
return () => unsubscribe(); return () => unsubscribe();
}, [user]); }, [user]);
// Ouvrir le modal automatiquement si on vient du Dashboard
useEffect(() => {
if (route?.params?.openModal) {
setModalVisible(true);
if (route.params.type) {
setType(route.params.type);
}
// Réinitialiser le paramètre
navigation.setParams({ openModal: false });
}
}, [route?.params]);
const loadCategories = async () => { const loadCategories = async () => {
if (!user) return; if (!user) return;
@@ -218,7 +230,16 @@ export const TransactionScreen = ({ route }: any) => {
label="Montant (€)" label="Montant (€)"
placeholder="0.00" placeholder="0.00"
value={amount} value={amount}
onChangeText={setAmount} onChangeText={(text) => {
// Permettre uniquement les chiffres et un point décimal
const cleaned = text.replace(/[^0-9.]/g, '');
// Empêcher plusieurs points
const parts = cleaned.split('.');
if (parts.length > 2) return;
// Limiter à 2 décimales
if (parts[1] && parts[1].length > 2) return;
setAmount(cleaned);
}}
keyboardType="decimal-pad" keyboardType="decimal-pad"
/> />
@@ -356,7 +377,8 @@ const styles = StyleSheet.create({
alignItems: 'center' alignItems: 'center'
}, },
typeButtonActive: { typeButtonActive: {
borderWidth: 2 borderWidth: 3,
backgroundColor: '#F0F7FF'
}, },
expenseButton: { expenseButton: {
borderColor: '#FF6B6B' borderColor: '#FF6B6B'
@@ -367,10 +389,11 @@ const styles = StyleSheet.create({
typeButtonText: { typeButtonText: {
fontSize: 16, fontSize: 16,
fontWeight: '600', fontWeight: '600',
color: '#666' color: '#999'
}, },
typeButtonTextActive: { typeButtonTextActive: {
color: '#333' color: '#333',
fontWeight: '700'
}, },
label: { label: {
fontSize: 14, fontSize: 14,
@@ -396,7 +419,13 @@ const styles = StyleSheet.create({
}, },
categoryItemActive: { categoryItemActive: {
backgroundColor: '#FFF', backgroundColor: '#FFF',
borderWidth: 2 borderWidth: 3,
transform: [{ scale: 1.05 }],
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.2,
shadowRadius: 4,
elevation: 4
}, },
categoryIcon: { categoryIcon: {
fontSize: 28, fontSize: 28,
@@ -405,6 +434,7 @@ const styles = StyleSheet.create({
categoryName: { categoryName: {
fontSize: 11, fontSize: 11,
color: '#666', color: '#666',
fontWeight: '600',
textAlign: 'center' textAlign: 'center'
}, },
submitButton: { submitButton: {