diff --git a/mobile/assets/icon.png b/mobile/assets/icon.png index 19e43d54..3ac5599d 100644 Binary files a/mobile/assets/icon.png and b/mobile/assets/icon.png differ diff --git a/mobile/src/context/CartContext.tsx b/mobile/src/context/CartContext.tsx index 8ec34e1e..c9ec6f41 100644 --- a/mobile/src/context/CartContext.tsx +++ b/mobile/src/context/CartContext.tsx @@ -1,183 +1,221 @@ -import React, { createContext, useContext, useState, useEffect, useCallback, type ReactNode } from 'react'; +import React, { + createContext, + useContext, + useState, + useEffect, + useCallback, + type ReactNode, +} from "react"; import { - getCart, addToCart as apiAddToCart, - removeFromCart as apiRemoveFromCart, clearCart as apiClearCart, -} from '../api/api'; -import { getToken } from '../auth/tokenStorage'; -import { extractUsernameFromToken } from '../auth/jwtUtils'; + getCart, + addToCart as apiAddToCart, + removeFromCart as apiRemoveFromCart, + clearCart as apiClearCart, +} from "../api/api"; +import { getToken } from "../auth/tokenStorage"; +import { extractUsernameFromToken } from "../auth/jwtUtils"; export interface CartItem { - id: number; - product_id: number; - name_product: string; - price: number; - quantity: number; // grammes - category: string; - image?: string; + id: number; + product_id: number; + name_product: string; + price: number; + quantity: number; + category: string; + image?: string; } interface ToastData { - message: string; - type: 'success' | 'error' | 'warning' | 'info'; + message: string; + type: "success" | "error" | "warning" | "info"; } interface CartContextType { - cartItems: CartItem[]; - addToCart: (item: Omit) => Promise; - removeFromCart: (id: number) => Promise; - clearCart: () => Promise; - cartCount: number; - cartTotal: number; - loading: boolean; - refreshCart: () => Promise; - toast: ToastData | null; - clearToast: () => void; - isAuthenticated: boolean; + cartItems: CartItem[]; + addToCart: (item: Omit) => Promise; + removeFromCart: (id: number) => Promise; + clearCart: () => Promise; + cartCount: number; + cartTotal: number; + loading: boolean; + refreshCart: () => Promise; + toast: ToastData | null; + clearToast: () => void; + isAuthenticated: boolean; } const CartContext = createContext(undefined); export function CartProvider({ children }: { children: ReactNode }) { - const [cartItems, setCartItems] = useState([]); - const [loading, setLoading] = useState(false); - const [toast, setToast] = useState(null); - const [isAuthenticated, setIsAuthenticated] = useState(false); + const [cartItems, setCartItems] = useState([]); + const [loading, setLoading] = useState(false); + const [toast, setToast] = useState(null); + const [isAuthenticated, setIsAuthenticated] = useState(false); - const showToast = (message: string, type: ToastData['type'] = 'success') => { - setToast({ message, type }); - setTimeout(() => setToast(null), 3000); - }; + const showToast = ( + message: string, + type: ToastData["type"] = "success", + ) => { + setToast({ message, type }); + setTimeout(() => setToast(null), 3000); + }; - const clearToast = () => setToast(null); + const clearToast = () => setToast(null); - const getUsername = async (): Promise => { - const token = await getToken(); - if (!token) return null; - return extractUsernameFromToken(token); - }; + const getUsername = async (): Promise => { + const token = await getToken(); + if (!token) return null; + return extractUsernameFromToken(token); + }; - const refreshCart = useCallback(async () => { - const username = await getUsername(); - if (!username) { - setCartItems([]); - setIsAuthenticated(false); - return; - } - setIsAuthenticated(true); - setLoading(true); - try { - const response = await getCart(username); - if (response.success && response.panier) { - const items: CartItem[] = response.panier.map((item: any) => ({ - id: item.id, - product_id: item.product_id, - name_product: item.product_name || item.name_product || 'Produit', - price: item.price, - quantity: item.quantity, - category: (item.category || 'autre').toLowerCase().trim(), - image: item.image, - })); - setCartItems(items); - } else { - setCartItems([]); - } - } catch { - setCartItems([]); - } finally { - setLoading(false); - } - }, []); + const refreshCart = useCallback(async () => { + const username = await getUsername(); + if (!username) { + setCartItems([]); + setIsAuthenticated(false); + return; + } + setIsAuthenticated(true); + setLoading(true); + try { + const response = await getCart(username); + if (response.success && response.panier) { + const items: CartItem[] = response.panier.map((item: any) => ({ + id: item.id, + product_id: item.product_id, + name_product: + item.product_name || item.name_product || "Produit", + price: item.price, + quantity: item.quantity, + category: (item.category || "autre").toLowerCase().trim(), + image: item.image, + })); + setCartItems(items); + } else { + setCartItems([]); + } + } catch { + setCartItems([]); + } finally { + setLoading(false); + } + }, []); - useEffect(() => { - refreshCart(); - }, [refreshCart]); + useEffect(() => { + refreshCart(); + }, [refreshCart]); - const addToCart = async (item: Omit) => { - const username = await getUsername(); - if (!username) { - showToast('Vous devez être connecté.', 'warning'); - return; - } - if (!item.quantity || item.quantity <= 0) { - showToast('Quantité invalide', 'error'); - return; - } - const cleanName = (item.name_product || 'Produit').replace(/\s*\([^)]*\)\s*/g, '').trim(); - setLoading(true); - try { - const response = await apiAddToCart({ - username, - name_product: cleanName, - category: (item.category || 'autre').toLowerCase().trim(), - quantity: Number(item.quantity), - price: Number(item.price) || 0, - }); - if (response.success) { - await refreshCart(); - showToast(`${cleanName} (${item.quantity}g) ajouté !`, 'success'); - } else { - showToast(response.message || "Erreur lors de l'ajout", 'error'); - } - } catch { - showToast("Erreur lors de l'ajout", 'error'); - } finally { - setLoading(false); - } - }; + const addToCart = async (item: Omit) => { + const username = await getUsername(); + if (!username) { + showToast("Vous devez être connecté.", "warning"); + return; + } + if (!item.quantity || item.quantity <= 0) { + showToast("Quantité invalide", "error"); + return; + } + const cleanName = (item.name_product || "Produit") + .replace(/\s*\([^)]*\)\s*/g, "") + .trim(); + setLoading(true); + try { + const response = await apiAddToCart({ + username, + name_product: cleanName, + category: (item.category || "autre").toLowerCase().trim(), + quantity: Number(item.quantity), + price: Number(item.price) || 0, + }); + if (response.success) { + await refreshCart(); + showToast( + `${cleanName} (${item.quantity}g) ajouté !`, + "success", + ); + } else { + showToast( + response.message || "Erreur lors de l'ajout", + "error", + ); + } + } catch { + showToast("Erreur lors de l'ajout", "error"); + } finally { + setLoading(false); + } + }; - const removeFromCart = async (id: number) => { - const username = await getUsername(); - if (!username) { showToast('Vous devez être connecté.', 'warning'); return; } - setLoading(true); - try { - const response = await apiRemoveFromCart(id, username); - if (response.success) { - await refreshCart(); - showToast('Produit supprimé', 'success'); - } else { - showToast(response.message || 'Erreur suppression', 'error'); - } - } catch { - showToast('Erreur suppression', 'error'); - } finally { - setLoading(false); - } - }; + const removeFromCart = async (id: number) => { + const username = await getUsername(); + if (!username) { + showToast("Vous devez être connecté.", "warning"); + return; + } + setLoading(true); + try { + const response = await apiRemoveFromCart(id, username); + if (response.success) { + await refreshCart(); + showToast("Produit supprimé", "success"); + } else { + showToast(response.message || "Erreur suppression", "error"); + } + } catch { + showToast("Erreur suppression", "error"); + } finally { + setLoading(false); + } + }; - const clearCartAction = async () => { - const username = await getUsername(); - if (!username) { showToast('Vous devez être connecté.', 'warning'); return; } - setLoading(true); - try { - const response = await apiClearCart(username); - if (response.success) { - setCartItems([]); - showToast(response.message || 'Panier vidé', 'success'); - } else { - showToast(response.message || 'Erreur vidage', 'error'); - } - } catch { - showToast('Erreur vidage', 'error'); - } finally { - setLoading(false); - } - }; + const clearCartAction = async () => { + const username = await getUsername(); + if (!username) { + showToast("Vous devez être connecté.", "warning"); + return; + } + setLoading(true); + try { + const response = await apiClearCart(username); + if (response.success) { + setCartItems([]); + showToast(response.message || "Panier vidé", "success"); + } else { + showToast(response.message || "Erreur vidage", "error"); + } + } catch { + showToast("Erreur vidage", "error"); + } finally { + setLoading(false); + } + }; - const cartCount = cartItems.length; - const cartTotal = cartItems.reduce((sum, item) => sum + item.price, 0); + const cartCount = cartItems.length; + const cartTotal = cartItems.reduce((sum, item) => sum + item.price, 0); - return ( - - {children} - - ); + return ( + + {children} + + ); } export function useCart() { - const context = useContext(CartContext); - if (!context) throw new Error('useCart must be used within a CartProvider'); - return context; + const context = useContext(CartContext); + if (!context) throw new Error("useCart must be used within a CartProvider"); + return context; } diff --git a/mobile/src/navigation/ClientNavigator.tsx b/mobile/src/navigation/ClientNavigator.tsx index e96c8a6a..ca7d79ef 100644 --- a/mobile/src/navigation/ClientNavigator.tsx +++ b/mobile/src/navigation/ClientNavigator.tsx @@ -67,7 +67,6 @@ function ClientTabs() { const navigation = useNavigation(); const [notifModalVisible, setNotifModalVisible] = useState(false); - // Naviguer vers le suivi quand on tap une push notification useEffect(() => { if (navigateToOrder) { setNotifModalVisible(false); @@ -77,7 +76,6 @@ function ClientTabs() { }, [navigateToOrder, navigation, clearNavigateToOrder]); const handleLogout = async () => { - // Supprimer le push token du backend avant logout if (pushToken) { await removePushTokenFromBackend(pushToken); } @@ -137,7 +135,6 @@ function ClientTabs() { gap: spacing.m, }} > - {/* Cloche notifications */} - {/* Modal notifications */}