From eb23fd4696295bba9b996e63252ae4c2430330ba Mon Sep 17 00:00:00 2001 From: Med Kamel Date: Wed, 23 Apr 2025 03:41:33 +0100 Subject: [PATCH] Cart Logic v2 --- app/_layout.tsx | 2 +- app/index.tsx | 112 ++----- app/screens/auth/OpeningScreen.tsx | 90 ++++++ app/screens/auth/SignIn-Screen.tsx | 26 +- app/screens/user/CartScreen.tsx | 291 ++++++++++++------ app/screens/user/GrainsScreen.tsx | 8 +- app/screens/user/ProductDetailsScreen.tsx | 22 +- app/screens/user/ProfileScreen.tsx | 58 +++- app/screens/user/UserHomeScreen.tsx | 23 +- app/screens/user/_layout.tsx | 2 +- {app/components => components}/CartItem.tsx | 48 +-- .../components => components}/ProductCard.tsx | 2 +- .../QuantityControl.tsx | 15 +- {app/constants => constants}/colors.ts | 0 {app/constants => constants}/types.ts | 1 - {app/context => context}/cartContext.tsx | 44 ++- firebase/auth.ts | 10 +- firebase/config.ts | 19 +- firebase/session.ts | 12 +- package-lock.json | 2 +- package.json | 2 +- app/utils/cartUtils.tsx => utils/cartUtils.ts | 2 +- 22 files changed, 507 insertions(+), 284 deletions(-) create mode 100644 app/screens/auth/OpeningScreen.tsx rename {app/components => components}/CartItem.tsx (74%) rename {app/components => components}/ProductCard.tsx (98%) rename {app/components => components}/QuantityControl.tsx (83%) rename {app/constants => constants}/colors.ts (100%) rename {app/constants => constants}/types.ts (94%) rename {app/context => context}/cartContext.tsx (53%) rename app/utils/cartUtils.tsx => utils/cartUtils.ts (90%) diff --git a/app/_layout.tsx b/app/_layout.tsx index e40127a..cb68712 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -3,7 +3,7 @@ import { Stack } from 'expo-router'; import { GestureHandlerRootView } from 'react-native-gesture-handler'; import { SafeAreaProvider } from 'react-native-safe-area-context'; import React from 'react'; -import { CartProvider } from './context/cartContext' +import { CartProvider } from '../context/cartContext' export default function Layout() { return ( diff --git a/app/index.tsx b/app/index.tsx index e413290..3b61465 100644 --- a/app/index.tsx +++ b/app/index.tsx @@ -1,90 +1,38 @@ -import React from 'react'; -import { View, Text, Image, StyleSheet, TouchableOpacity } from 'react-native'; +import React, { useEffect, useState } from 'react'; +import { View, ActivityIndicator } from 'react-native'; +import { getAuth, onAuthStateChanged } from 'firebase/auth'; import { router } from 'expo-router'; -import COLORS from './constants/colors'; -import { StatusBar } from 'expo-status-bar'; +import OpeningScreen from './screens/auth/OpeningScreen'; // ajuste le chemin si besoin +const Index = () => { + const [checkingAuth, setCheckingAuth] = useState(true); + const [isLoggedIn, setIsLoggedIn] = useState(false); -const OpeningScreen = () => { + useEffect(() => { + const auth = getAuth(); - return ( - - - - Bienvenue chez Brix Café - - - Depuis 2024, Brix Café vous fait vivre une expérience café unique, - inspirée du savoir-faire italien et portée par une passion authentique. - Des grains d’exception, une qualité incomparable. - + const unsubscribe = onAuthStateChanged(auth, (user) => { + if (user) { + setIsLoggedIn(true); + router.replace('/screens/user/UserHomeScreen'); // redirige vers Home + } else { + setIsLoggedIn(false); // utilisateur non connecté + } + setCheckingAuth(false); // vérification terminée + }); - router.push('/screens/auth/SignIn-Screen')}> - Se connecter - + return () => unsubscribe(); + }, []); - router.push('/screens/auth/SignUpScreen')}> - Créer un compte - - - ); + if (checkingAuth) { + return ( + + + + ); + } + + return !isLoggedIn ? : null; }; -// Styles -const styles = StyleSheet.create({ - container: { - flex: 1, - backgroundColor: '#000000', - alignItems: 'center', - justifyContent: 'center', - paddingHorizontal: 20, - }, - logo: { - width: 120, - height: 120, - marginBottom: 20, - }, - welcomeText: { - fontSize: 26, - fontWeight: 'bold', - color: COLORS.text, - marginBottom: 20, - }, - coffeeImage: { - width: '120%', - height: 150, - marginTop:40, - marginBottom: 30, - }, - descriptionText: { - fontSize: 14, - color: COLORS.text, - textAlign: 'center', - marginBottom: 40, - }, - signInButton: { - backgroundColor: COLORS.primary, - paddingVertical: 15, - paddingHorizontal: 40, - borderRadius: 10, - marginBottom: 20, - width: '80%', - alignItems: 'center', - }, - signUpButton: { - borderWidth: 1, - borderColor: COLORS.primary, - paddingVertical: 15, - paddingHorizontal: 40, - borderRadius: 10, - width: '80%', - alignItems: 'center', - }, - buttonText: { - fontSize: 16, - color: COLORS.text, - fontWeight: 'bold', - }, -}); - -export default OpeningScreen; +export default Index; diff --git a/app/screens/auth/OpeningScreen.tsx b/app/screens/auth/OpeningScreen.tsx new file mode 100644 index 0000000..f7bfde7 --- /dev/null +++ b/app/screens/auth/OpeningScreen.tsx @@ -0,0 +1,90 @@ +import React from 'react'; +import { View, Text, Image, StyleSheet, TouchableOpacity } from 'react-native'; +import { router } from 'expo-router'; +import COLORS from '@/constants/colors'; +import { StatusBar } from 'expo-status-bar'; + + +const OpeningScreen = () => { + + return ( + + + + Bienvenue chez Brix Café + + + Depuis 2024, Brix Café vous fait vivre une expérience café unique, + inspirée du savoir-faire italien et portée par une passion authentique. + Des grains d’exception, une qualité incomparable. + + + router.push('/screens/auth/SignIn-Screen')}> + Se connecter + + + router.push('/screens/auth/SignUpScreen')}> + Créer un compte + + + ); +}; + +// Styles +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: '#000000', + alignItems: 'center', + justifyContent: 'center', + paddingHorizontal: 20, + }, + logo: { + width: 120, + height: 120, + marginBottom: 20, + }, + welcomeText: { + fontSize: 26, + fontWeight: 'bold', + color: COLORS.text, + marginBottom: 20, + }, + coffeeImage: { + width: '120%', + height: 150, + marginTop:40, + marginBottom: 30, + }, + descriptionText: { + fontSize: 14, + color: COLORS.text, + textAlign: 'center', + marginBottom: 40, + }, + signInButton: { + backgroundColor: COLORS.primary, + paddingVertical: 15, + paddingHorizontal: 40, + borderRadius: 10, + marginBottom: 20, + width: '80%', + alignItems: 'center', + }, + signUpButton: { + borderWidth: 1, + borderColor: COLORS.primary, + paddingVertical: 15, + paddingHorizontal: 40, + borderRadius: 10, + width: '80%', + alignItems: 'center', + }, + buttonText: { + fontSize: 16, + color: COLORS.text, + fontWeight: 'bold', + }, +}); + +export default OpeningScreen; diff --git a/app/screens/auth/SignIn-Screen.tsx b/app/screens/auth/SignIn-Screen.tsx index c0d09b7..60542a4 100644 --- a/app/screens/auth/SignIn-Screen.tsx +++ b/app/screens/auth/SignIn-Screen.tsx @@ -5,8 +5,8 @@ import { router } from "expo-router"; import { signIn } from "../../../firebase/auth"; // Assure-toi que le chemin est correct import { Link } from "expo-router"; import { KeyboardAwareScrollView } from "react-native-keyboard-aware-scroll-view"; // Import the library -import COLORS from "@/app/constants/colors"; - +import COLORS from "@/constants/colors"; +import AsyncStorage from "@react-native-async-storage/async-storage"; const SignInScreen = () => { @@ -43,13 +43,21 @@ const SignInScreen = () => { const handleLogin = async () => { if (!validateForm()) return; - + try { - const { user } = await signIn(form.email, form.password); // Destructure to get user - console.log("Connexion réussie :", user.email); // Access the email directly + const { user } = await signIn(form.email, form.password); + + console.log("Connexion réussie :", user.email); + + if (form.rememberMe) { + await AsyncStorage.setItem("rememberMe", "true"); + } else { + await AsyncStorage.removeItem("rememberMe"); + } + router.replace("/screens/user/UserHomeScreen"); } catch (error: any) { - Alert.alert("Erreur", error.message); // Display the error message + Alert.alert("Erreur", error.message); } }; @@ -67,7 +75,7 @@ const SignInScreen = () => { > - + Se connecter @@ -117,9 +125,9 @@ const SignInScreen = () => { style={styles.eyeIcon} > {showPassword ? ( - - ) : ( + ) : ( + )} diff --git a/app/screens/user/CartScreen.tsx b/app/screens/user/CartScreen.tsx index c3b0544..40a8d12 100644 --- a/app/screens/user/CartScreen.tsx +++ b/app/screens/user/CartScreen.tsx @@ -1,131 +1,238 @@ -import React, { useEffect, useState } from 'react'; -import { useRouter } from 'expo-router'; -import { View, Text, Button, Alert, StyleSheet } from 'react-native'; +import React, { useEffect, useState, useCallback } from 'react'; +import { + View, + Text, + Alert, + StyleSheet, + FlatList, + Platform, + TouchableOpacity, + Button, +} from 'react-native'; +import { SafeAreaView } from 'react-native-safe-area-context'; +import { useCart } from '@/context/cartContext'; import { getAuth } from 'firebase/auth'; -import { collection, addDoc } from 'firebase/firestore'; // Make sure these are imported -import { db } from '@/firebase/config'; // Ensure this path is correct -import { useCart } from '@/app/context/cartContext'; +import { collection, addDoc } from 'firebase/firestore'; +import { db } from '@/firebase/config'; +import CartItem from '@/components/CartItem'; +import { ArrowLeft } from 'lucide-react-native'; +import Animated, { + useAnimatedStyle, + useSharedValue, + withSequence, + withTiming, +} from 'react-native-reanimated'; +import * as Haptics from 'expo-haptics'; +import { useRouter } from 'expo-router'; +import COLORS from '@/constants/colors'; +import { CartItem as CartItemType } from '@/context/cartContext'; -const CartScreen = () => { - const { cart, clearCart } = useCart(); +export default function CartScreen() { + const { cart, clearCart, updateQuantity } = useCart(); + const totalAmount = cart.total; + const [isProcessingOrder, setIsProcessingOrder] = useState(false); const auth = getAuth(); - const [isAuthenticated, setIsAuthenticated] = useState(false); const router = useRouter(); - // Check if the user is logged in when the component mounts - useEffect(() => { - const user = auth.currentUser; - if (user) { - setIsAuthenticated(true); - } else { - setIsAuthenticated(false); - router.push('../screens/auth/SignInScreen'); // Redirect to sign-in screen if not logged in - } - }, [auth, router]); + const totalValue = useSharedValue(1); + const buttonScale = useSharedValue(1); - const handleConfirmOrder = async () => { - if (!isAuthenticated) { - Alert.alert('Erreur', 'Utilisateur non connecté.'); - return; - } + - if (cart.total <= 0) { + const totalAnimatedStyle = useAnimatedStyle(() => ({ + transform: [{ scale: totalValue.value }], + })); + + const handleConfirmOrder = useCallback(async () => { + + + if (totalAmount <= 0) { Alert.alert('Panier vide', 'Veuillez ajouter des produits à votre panier'); return; } - try { - const userId = auth.currentUser?.uid; - if (!userId) { - Alert.alert('Erreur', 'Utilisateur non connecté.'); - return; - } + setIsProcessingOrder(true); + buttonScale.value = withSequence( + withTiming(0.95, { duration: 100 }), + withTiming(1, { duration: 150 }) + ); + if (Platform.OS !== 'web') { + Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success); + } + + try { const orderData: any = { - userId, status: 'En attente', + createdAt: new Date().toISOString(), + totalAmount: totalAmount }; cart.items.forEach((item, index) => { - orderData[`item${index + 1}`] = [item.quantity, item.productName]; + orderData[`item${index + 1}`] = { + name: item.name, + quantity: item.quantity, + }; }); await addDoc(collection(db, 'orders'), orderData); - - Alert.alert('Commande confirmée', 'Votre commande a été enregistrée.'); + Alert.alert('Commande confirmée', `Montant Total : ${totalAmount}DT`); clearCart(); } catch (error) { - console.error('Erreur de commande', error); + console.error('Erreur commande :', error); Alert.alert('Erreur', "Impossible d'enregistrer la commande."); + } finally { + setIsProcessingOrder(false); } - }; + }, [cart, totalAmount]); - if (!isAuthenticated) { - return Chargement...; // Loading state while checking authentication - } + const renderItem = useCallback(({ item }: { item: CartItemType }) => ( + { + updateQuantity(id, newQty); + totalValue.value = withSequence( + withTiming(1.05, { duration: 100 }), + withTiming(1, { duration: 100 }) + ); + }} + /> + ), []); + + const keyExtractor = useCallback((item: CartItemType) => item.id, []); return ( - - Votre Panier + + + router.back()}> + + + Mon Panier + - {cart.items.length === 0 ? ( - Votre panier est vide. - ) : ( - cart.items.map((item, index) => ( - - - {item.productName} - Quantité : {item.quantity} - - - )) - )} + + + Votre panier est vide + + } + /> + - {cart.items.length > 0 && ( - -