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 {
|
import {
|
||||||
getCart, addToCart as apiAddToCart,
|
getCart,
|
||||||
removeFromCart as apiRemoveFromCart, clearCart as apiClearCart,
|
addToCart as apiAddToCart,
|
||||||
} from '../api/api';
|
removeFromCart as apiRemoveFromCart,
|
||||||
import { getToken } from '../auth/tokenStorage';
|
clearCart as apiClearCart,
|
||||||
import { extractUsernameFromToken } from '../auth/jwtUtils';
|
} from "../api/api";
|
||||||
|
import { getToken } from "../auth/tokenStorage";
|
||||||
|
import { extractUsernameFromToken } from "../auth/jwtUtils";
|
||||||
|
|
||||||
export interface CartItem {
|
export interface CartItem {
|
||||||
id: number;
|
id: number;
|
||||||
product_id: number;
|
product_id: number;
|
||||||
name_product: string;
|
name_product: string;
|
||||||
price: number;
|
price: number;
|
||||||
quantity: number; // grammes
|
quantity: number;
|
||||||
category: string;
|
category: string;
|
||||||
image?: string;
|
image?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ToastData {
|
interface ToastData {
|
||||||
message: string;
|
message: string;
|
||||||
type: 'success' | 'error' | 'warning' | 'info';
|
type: "success" | "error" | "warning" | "info";
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CartContextType {
|
interface CartContextType {
|
||||||
cartItems: CartItem[];
|
cartItems: CartItem[];
|
||||||
addToCart: (item: Omit<CartItem, 'id'>) => Promise<void>;
|
addToCart: (item: Omit<CartItem, "id">) => Promise<void>;
|
||||||
removeFromCart: (id: number) => Promise<void>;
|
removeFromCart: (id: number) => Promise<void>;
|
||||||
clearCart: () => Promise<void>;
|
clearCart: () => Promise<void>;
|
||||||
cartCount: number;
|
cartCount: number;
|
||||||
cartTotal: number;
|
cartTotal: number;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
refreshCart: () => Promise<void>;
|
refreshCart: () => Promise<void>;
|
||||||
toast: ToastData | null;
|
toast: ToastData | null;
|
||||||
clearToast: () => void;
|
clearToast: () => void;
|
||||||
isAuthenticated: boolean;
|
isAuthenticated: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CartContext = createContext<CartContextType | undefined>(undefined);
|
const CartContext = createContext<CartContextType | undefined>(undefined);
|
||||||
|
|
||||||
export function CartProvider({ children }: { children: ReactNode }) {
|
export function CartProvider({ children }: { children: ReactNode }) {
|
||||||
const [cartItems, setCartItems] = useState<CartItem[]>([]);
|
const [cartItems, setCartItems] = useState<CartItem[]>([]);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [toast, setToast] = useState<ToastData | null>(null);
|
const [toast, setToast] = useState<ToastData | null>(null);
|
||||||
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
||||||
|
|
||||||
const showToast = (message: string, type: ToastData['type'] = 'success') => {
|
const showToast = (
|
||||||
setToast({ message, type });
|
message: string,
|
||||||
setTimeout(() => setToast(null), 3000);
|
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 getUsername = async (): Promise<string | null> => {
|
||||||
const token = await getToken();
|
const token = await getToken();
|
||||||
if (!token) return null;
|
if (!token) return null;
|
||||||
return extractUsernameFromToken(token);
|
return extractUsernameFromToken(token);
|
||||||
};
|
};
|
||||||
|
|
||||||
const refreshCart = useCallback(async () => {
|
const refreshCart = useCallback(async () => {
|
||||||
const username = await getUsername();
|
const username = await getUsername();
|
||||||
if (!username) {
|
if (!username) {
|
||||||
setCartItems([]);
|
setCartItems([]);
|
||||||
setIsAuthenticated(false);
|
setIsAuthenticated(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setIsAuthenticated(true);
|
setIsAuthenticated(true);
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
const response = await getCart(username);
|
const response = await getCart(username);
|
||||||
if (response.success && response.panier) {
|
if (response.success && response.panier) {
|
||||||
const items: CartItem[] = response.panier.map((item: any) => ({
|
const items: CartItem[] = response.panier.map((item: any) => ({
|
||||||
id: item.id,
|
id: item.id,
|
||||||
product_id: item.product_id,
|
product_id: item.product_id,
|
||||||
name_product: item.product_name || item.name_product || 'Produit',
|
name_product:
|
||||||
price: item.price,
|
item.product_name || item.name_product || "Produit",
|
||||||
quantity: item.quantity,
|
price: item.price,
|
||||||
category: (item.category || 'autre').toLowerCase().trim(),
|
quantity: item.quantity,
|
||||||
image: item.image,
|
category: (item.category || "autre").toLowerCase().trim(),
|
||||||
}));
|
image: item.image,
|
||||||
setCartItems(items);
|
}));
|
||||||
} else {
|
setCartItems(items);
|
||||||
setCartItems([]);
|
} else {
|
||||||
}
|
setCartItems([]);
|
||||||
} catch {
|
}
|
||||||
setCartItems([]);
|
} catch {
|
||||||
} finally {
|
setCartItems([]);
|
||||||
setLoading(false);
|
} finally {
|
||||||
}
|
setLoading(false);
|
||||||
}, []);
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
refreshCart();
|
refreshCart();
|
||||||
}, [refreshCart]);
|
}, [refreshCart]);
|
||||||
|
|
||||||
const addToCart = async (item: Omit<CartItem, 'id'>) => {
|
const addToCart = async (item: Omit<CartItem, "id">) => {
|
||||||
const username = await getUsername();
|
const username = await getUsername();
|
||||||
if (!username) {
|
if (!username) {
|
||||||
showToast('Vous devez être connecté.', 'warning');
|
showToast("Vous devez être connecté.", "warning");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!item.quantity || item.quantity <= 0) {
|
if (!item.quantity || item.quantity <= 0) {
|
||||||
showToast('Quantité invalide', 'error');
|
showToast("Quantité invalide", "error");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const cleanName = (item.name_product || 'Produit').replace(/\s*\([^)]*\)\s*/g, '').trim();
|
const cleanName = (item.name_product || "Produit")
|
||||||
setLoading(true);
|
.replace(/\s*\([^)]*\)\s*/g, "")
|
||||||
try {
|
.trim();
|
||||||
const response = await apiAddToCart({
|
setLoading(true);
|
||||||
username,
|
try {
|
||||||
name_product: cleanName,
|
const response = await apiAddToCart({
|
||||||
category: (item.category || 'autre').toLowerCase().trim(),
|
username,
|
||||||
quantity: Number(item.quantity),
|
name_product: cleanName,
|
||||||
price: Number(item.price) || 0,
|
category: (item.category || "autre").toLowerCase().trim(),
|
||||||
});
|
quantity: Number(item.quantity),
|
||||||
if (response.success) {
|
price: Number(item.price) || 0,
|
||||||
await refreshCart();
|
});
|
||||||
showToast(`${cleanName} (${item.quantity}g) ajouté !`, 'success');
|
if (response.success) {
|
||||||
} else {
|
await refreshCart();
|
||||||
showToast(response.message || "Erreur lors de l'ajout", 'error');
|
showToast(
|
||||||
}
|
`${cleanName} (${item.quantity}g) ajouté !`,
|
||||||
} catch {
|
"success",
|
||||||
showToast("Erreur lors de l'ajout", 'error');
|
);
|
||||||
} finally {
|
} else {
|
||||||
setLoading(false);
|
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 removeFromCart = async (id: number) => {
|
||||||
const username = await getUsername();
|
const username = await getUsername();
|
||||||
if (!username) { showToast('Vous devez être connecté.', 'warning'); return; }
|
if (!username) {
|
||||||
setLoading(true);
|
showToast("Vous devez être connecté.", "warning");
|
||||||
try {
|
return;
|
||||||
const response = await apiRemoveFromCart(id, username);
|
}
|
||||||
if (response.success) {
|
setLoading(true);
|
||||||
await refreshCart();
|
try {
|
||||||
showToast('Produit supprimé', 'success');
|
const response = await apiRemoveFromCart(id, username);
|
||||||
} else {
|
if (response.success) {
|
||||||
showToast(response.message || 'Erreur suppression', 'error');
|
await refreshCart();
|
||||||
}
|
showToast("Produit supprimé", "success");
|
||||||
} catch {
|
} else {
|
||||||
showToast('Erreur suppression', 'error');
|
showToast(response.message || "Erreur suppression", "error");
|
||||||
} finally {
|
}
|
||||||
setLoading(false);
|
} catch {
|
||||||
}
|
showToast("Erreur suppression", "error");
|
||||||
};
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const clearCartAction = async () => {
|
const clearCartAction = async () => {
|
||||||
const username = await getUsername();
|
const username = await getUsername();
|
||||||
if (!username) { showToast('Vous devez être connecté.', 'warning'); return; }
|
if (!username) {
|
||||||
setLoading(true);
|
showToast("Vous devez être connecté.", "warning");
|
||||||
try {
|
return;
|
||||||
const response = await apiClearCart(username);
|
}
|
||||||
if (response.success) {
|
setLoading(true);
|
||||||
setCartItems([]);
|
try {
|
||||||
showToast(response.message || 'Panier vidé', 'success');
|
const response = await apiClearCart(username);
|
||||||
} else {
|
if (response.success) {
|
||||||
showToast(response.message || 'Erreur vidage', 'error');
|
setCartItems([]);
|
||||||
}
|
showToast(response.message || "Panier vidé", "success");
|
||||||
} catch {
|
} else {
|
||||||
showToast('Erreur vidage', 'error');
|
showToast(response.message || "Erreur vidage", "error");
|
||||||
} finally {
|
}
|
||||||
setLoading(false);
|
} catch {
|
||||||
}
|
showToast("Erreur vidage", "error");
|
||||||
};
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const cartCount = cartItems.length;
|
const cartCount = cartItems.length;
|
||||||
const cartTotal = cartItems.reduce((sum, item) => sum + item.price, 0);
|
const cartTotal = cartItems.reduce((sum, item) => sum + item.price, 0);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CartContext.Provider value={{
|
<CartContext.Provider
|
||||||
cartItems, addToCart, removeFromCart, clearCart: clearCartAction,
|
value={{
|
||||||
cartCount, cartTotal, loading, refreshCart, toast, clearToast, isAuthenticated,
|
cartItems,
|
||||||
}}>
|
addToCart,
|
||||||
{children}
|
removeFromCart,
|
||||||
</CartContext.Provider>
|
clearCart: clearCartAction,
|
||||||
);
|
cartCount,
|
||||||
|
cartTotal,
|
||||||
|
loading,
|
||||||
|
refreshCart,
|
||||||
|
toast,
|
||||||
|
clearToast,
|
||||||
|
isAuthenticated,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</CartContext.Provider>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useCart() {
|
export function useCart() {
|
||||||
const context = useContext(CartContext);
|
const context = useContext(CartContext);
|
||||||
if (!context) throw new Error('useCart must be used within a CartProvider');
|
if (!context) throw new Error("useCart must be used within a CartProvider");
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,7 +67,6 @@ function ClientTabs() {
|
|||||||
const navigation = useNavigation<any>();
|
const navigation = useNavigation<any>();
|
||||||
const [notifModalVisible, setNotifModalVisible] = useState(false);
|
const [notifModalVisible, setNotifModalVisible] = useState(false);
|
||||||
|
|
||||||
// Naviguer vers le suivi quand on tap une push notification
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (navigateToOrder) {
|
if (navigateToOrder) {
|
||||||
setNotifModalVisible(false);
|
setNotifModalVisible(false);
|
||||||
@@ -77,7 +76,6 @@ function ClientTabs() {
|
|||||||
}, [navigateToOrder, navigation, clearNavigateToOrder]);
|
}, [navigateToOrder, navigation, clearNavigateToOrder]);
|
||||||
|
|
||||||
const handleLogout = async () => {
|
const handleLogout = async () => {
|
||||||
// Supprimer le push token du backend avant logout
|
|
||||||
if (pushToken) {
|
if (pushToken) {
|
||||||
await removePushTokenFromBackend(pushToken);
|
await removePushTokenFromBackend(pushToken);
|
||||||
}
|
}
|
||||||
@@ -137,7 +135,6 @@ function ClientTabs() {
|
|||||||
gap: spacing.m,
|
gap: spacing.m,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* Cloche notifications */}
|
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
onPress={openNotifModal}
|
onPress={openNotifModal}
|
||||||
style={{ position: "relative" }}
|
style={{ position: "relative" }}
|
||||||
@@ -247,7 +244,6 @@ function ClientTabs() {
|
|||||||
/>
|
/>
|
||||||
</Tab.Navigator>
|
</Tab.Navigator>
|
||||||
|
|
||||||
{/* Modal notifications */}
|
|
||||||
<Modal
|
<Modal
|
||||||
visible={notifModalVisible}
|
visible={notifModalVisible}
|
||||||
animationType="slide"
|
animationType="slide"
|
||||||
|
|||||||
Reference in New Issue
Block a user