chore: update icon
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 952 KiB After Width: | Height: | Size: 761 KiB |
+193
-155
@@ -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<CartItem, 'id'>) => Promise<void>;
|
||||
removeFromCart: (id: number) => Promise<void>;
|
||||
clearCart: () => Promise<void>;
|
||||
cartCount: number;
|
||||
cartTotal: number;
|
||||
loading: boolean;
|
||||
refreshCart: () => Promise<void>;
|
||||
toast: ToastData | null;
|
||||
clearToast: () => void;
|
||||
isAuthenticated: boolean;
|
||||
cartItems: CartItem[];
|
||||
addToCart: (item: Omit<CartItem, "id">) => Promise<void>;
|
||||
removeFromCart: (id: number) => Promise<void>;
|
||||
clearCart: () => Promise<void>;
|
||||
cartCount: number;
|
||||
cartTotal: number;
|
||||
loading: boolean;
|
||||
refreshCart: () => Promise<void>;
|
||||
toast: ToastData | null;
|
||||
clearToast: () => void;
|
||||
isAuthenticated: boolean;
|
||||
}
|
||||
|
||||
const CartContext = createContext<CartContextType | undefined>(undefined);
|
||||
|
||||
export function CartProvider({ children }: { children: ReactNode }) {
|
||||
const [cartItems, setCartItems] = useState<CartItem[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [toast, setToast] = useState<ToastData | null>(null);
|
||||
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
||||
const [cartItems, setCartItems] = useState<CartItem[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [toast, setToast] = useState<ToastData | null>(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<string | null> => {
|
||||
const token = await getToken();
|
||||
if (!token) return null;
|
||||
return extractUsernameFromToken(token);
|
||||
};
|
||||
const getUsername = async (): Promise<string | null> => {
|
||||
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<CartItem, 'id'>) => {
|
||||
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<CartItem, "id">) => {
|
||||
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 (
|
||||
<CartContext.Provider value={{
|
||||
cartItems, addToCart, removeFromCart, clearCart: clearCartAction,
|
||||
cartCount, cartTotal, loading, refreshCart, toast, clearToast, isAuthenticated,
|
||||
}}>
|
||||
{children}
|
||||
</CartContext.Provider>
|
||||
);
|
||||
return (
|
||||
<CartContext.Provider
|
||||
value={{
|
||||
cartItems,
|
||||
addToCart,
|
||||
removeFromCart,
|
||||
clearCart: clearCartAction,
|
||||
cartCount,
|
||||
cartTotal,
|
||||
loading,
|
||||
refreshCart,
|
||||
toast,
|
||||
clearToast,
|
||||
isAuthenticated,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</CartContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -67,7 +67,6 @@ function ClientTabs() {
|
||||
const navigation = useNavigation<any>();
|
||||
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 */}
|
||||
<TouchableOpacity
|
||||
onPress={openNotifModal}
|
||||
style={{ position: "relative" }}
|
||||
@@ -247,7 +244,6 @@ function ClientTabs() {
|
||||
/>
|
||||
</Tab.Navigator>
|
||||
|
||||
{/* Modal notifications */}
|
||||
<Modal
|
||||
visible={notifModalVisible}
|
||||
animationType="slide"
|
||||
|
||||
Reference in New Issue
Block a user