chore: fix
This commit is contained in:
@@ -330,8 +330,8 @@ export const changePassword = async (
|
||||
export interface BasketResponse {
|
||||
success: boolean;
|
||||
message?: string;
|
||||
panier?: any[];
|
||||
data?: { panier?: any[] };
|
||||
panier?: Record<string, unknown>[];
|
||||
data?: { panier?: Record<string, unknown>[] };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -569,7 +569,7 @@ export const clearCart = async (username: string) => {
|
||||
success: false,
|
||||
message: errorData.error || "Erreur vidage",
|
||||
};
|
||||
} catch (parseError) {
|
||||
} catch {
|
||||
// Si le parsing JSON échoue (HTML retourné)
|
||||
console.error("❌ [CLEAR] Réponse non-JSON du serveur");
|
||||
return {
|
||||
@@ -1206,7 +1206,7 @@ export const getOrderTotal = async (commandId: number): Promise<number> => {
|
||||
}
|
||||
};
|
||||
|
||||
export const calculateOrderTotal = (order: any): number => {
|
||||
export const calculateOrderTotal = (order: Record<string, unknown>): number => {
|
||||
// Préférer total (colonne calculée par le backend)
|
||||
if (typeof order.total === "number" && order.total > 0) {
|
||||
return order.total;
|
||||
@@ -1219,7 +1219,7 @@ export const calculateOrderTotal = (order: any): number => {
|
||||
|
||||
// Fallback: calculer depuis les items si présents
|
||||
if (Array.isArray(order.items) && order.items.length > 0) {
|
||||
return order.items.reduce((sum: number, item: any) => {
|
||||
return (order.items as Record<string, unknown>[]).reduce((sum: number, item) => {
|
||||
const price = item.prix || item.price || 0;
|
||||
const quantity = item.quantite || item.quantity || 1;
|
||||
return sum + price * quantity;
|
||||
|
||||
@@ -19,6 +19,7 @@ export interface ApiResponse {
|
||||
token_type?: string;
|
||||
expires_in?: number;
|
||||
user?: UserResponse;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
[key: string]: any; // Pour les champs additionnels
|
||||
}
|
||||
|
||||
@@ -258,6 +259,7 @@ export interface OrderDetail {
|
||||
// Métadonnées
|
||||
payment_method?: string;
|
||||
notes?: string;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
@@ -352,6 +354,7 @@ export interface TrackingResponse {
|
||||
updated_at?: number;
|
||||
created_at?: string;
|
||||
message?: string;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
@@ -379,6 +382,7 @@ export interface Product {
|
||||
}>;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
@@ -388,6 +392,7 @@ export interface ProductsResponse {
|
||||
data?: Product[];
|
||||
products?: Product[];
|
||||
count?: number;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
@@ -429,6 +434,7 @@ export interface JWTPayload {
|
||||
iat: number;
|
||||
exp: number;
|
||||
iss: string;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
@@ -445,6 +451,7 @@ export interface ErrorResponse {
|
||||
message?: string;
|
||||
details?: string;
|
||||
status_code?: number;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
@@ -607,6 +614,7 @@ export interface ETAResponse {
|
||||
eta_available?: boolean;
|
||||
livreur_distance?: number;
|
||||
message?: string;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
@@ -767,6 +775,7 @@ export interface ConfirmReceptionResponse {
|
||||
data?: {
|
||||
category?: string;
|
||||
points_earned?: number;
|
||||
[key: string]: any;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
[key: string]: any;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -128,12 +128,12 @@ function Navbar() {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
});
|
||||
} catch (_) {}
|
||||
} catch { /* noop */ }
|
||||
}
|
||||
sessionStorage.removeItem("admin_token");
|
||||
sessionStorage.removeItem("admin_username");
|
||||
navigate("/login/client", { replace: true });
|
||||
} catch (_) {
|
||||
} catch {
|
||||
sessionStorage.removeItem("admin_token");
|
||||
sessionStorage.removeItem("admin_username");
|
||||
navigate("/login/client", { replace: true });
|
||||
|
||||
@@ -54,6 +54,7 @@ interface ToastMessage {
|
||||
type: "success" | "error" | "warning" | "info";
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-refresh/only-export-components
|
||||
export const CartContext = createContext<CartContextType | undefined>(undefined);
|
||||
|
||||
export function CartProvider({ children }: { children: ReactNode }) {
|
||||
@@ -168,7 +169,7 @@ export function CartProvider({ children }: { children: ReactNode }) {
|
||||
return () => {
|
||||
console.log("🔄 [CART] CartProvider unmount");
|
||||
};
|
||||
}, []);
|
||||
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
/**
|
||||
* ✅ Ajouter au panier
|
||||
|
||||
@@ -36,6 +36,7 @@ export function ThemeProvider({ children }: { children: ReactNode }) {
|
||||
);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-refresh/only-export-components
|
||||
export function useTheme(): ThemeContextValue {
|
||||
return useContext(ThemeContext);
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ function UserAccueil() {
|
||||
return;
|
||||
}
|
||||
loadProducts();
|
||||
}, [selectedCategory, categories]);
|
||||
}, [selectedCategory, categories]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
const loadProducts = async () => {
|
||||
setLoading(true);
|
||||
@@ -58,8 +58,8 @@ function UserAccueil() {
|
||||
} else {
|
||||
setProducts([]);
|
||||
}
|
||||
} catch (err: any) {
|
||||
setError(err.message || "Erreur lors du chargement des produits");
|
||||
} catch (err: unknown) {
|
||||
setError(err instanceof Error ? err.message : "Erreur lors du chargement des produits");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ function Cart() {
|
||||
useEffect(() => {
|
||||
if (!isUserAuthenticated()) { navigate("/login/client", { replace: true }); return; }
|
||||
refreshCart();
|
||||
}, []);
|
||||
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
const total = cartItems.reduce((sum, item) => sum + item.price, 0);
|
||||
|
||||
@@ -71,7 +71,7 @@ function Cart() {
|
||||
const res = await getProductById(item.product_id);
|
||||
if (res.success && res.data) {
|
||||
const p = res.data;
|
||||
const videoMedia = p.media?.find((m: any) => m && m.type === "video");
|
||||
const videoMedia = p.media?.find((m) => m && m.type === "video");
|
||||
return {
|
||||
...item,
|
||||
image: getProductImage(p),
|
||||
@@ -89,7 +89,7 @@ function Cart() {
|
||||
}
|
||||
};
|
||||
enrich();
|
||||
}, [cartItems]);
|
||||
}, [cartItems]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
const handleClearCart = async () => {
|
||||
if (!isUserAuthenticated()) { navigate("/login/client", { replace: true }); return; }
|
||||
|
||||
@@ -66,7 +66,7 @@ function Checkout() {
|
||||
const res = await getProductById(item.product_id);
|
||||
if (res.success && res.data) {
|
||||
const p: Product = res.data;
|
||||
const img = p.media?.find((m: any) => m && m.type === 'image');
|
||||
const img = p.media?.find((m) => m && m.type === 'image');
|
||||
if (img?.url) {
|
||||
setItemImages((prev) => ({ ...prev, [item.id]: getMediaUrl(img.url) }));
|
||||
}
|
||||
@@ -289,7 +289,7 @@ function Checkout() {
|
||||
if (response.success && response.payment_method === 'crypto') {
|
||||
setCryptoPaymentData({
|
||||
command_id: response.command_id!,
|
||||
client_order_number: (response as any).client_order_number,
|
||||
client_order_number: (response as Record<string, unknown>).client_order_number as number | undefined,
|
||||
payment_status: response.payment_status!,
|
||||
pay_address: response.pay_address!,
|
||||
pay_amount: response.pay_amount!,
|
||||
@@ -348,13 +348,13 @@ function Checkout() {
|
||||
// ✅ Préparer les données pour le modal
|
||||
setConfirmationData({
|
||||
command_id,
|
||||
client_order_number: (response as any).client_order_number,
|
||||
client_order_number: (response as Record<string, unknown>).client_order_number as number | undefined,
|
||||
assigned_to,
|
||||
queue_info,
|
||||
delivery_address: delivery_address || address,
|
||||
arrivalTime,
|
||||
total: frontendTotal,
|
||||
referral_used: (response as any).referral_used,
|
||||
referral_used: (response as Record<string, unknown>).referral_used as number | undefined,
|
||||
clientInfo: {
|
||||
first_name: firstName,
|
||||
last_name: lastName,
|
||||
@@ -369,9 +369,9 @@ function Checkout() {
|
||||
} else {
|
||||
setError(response.message || '❌ Erreur lors de la validation de la commande');
|
||||
}
|
||||
} catch (err: any) {
|
||||
} catch (err: unknown) {
|
||||
console.error('❌ Erreur checkout:', err);
|
||||
setError(err.message || '❌ Erreur serveur. Veuillez réessayer.');
|
||||
setError(err instanceof Error ? err.message : '❌ Erreur serveur. Veuillez réessayer.');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ function ConsultationHistorique() {
|
||||
getReferralBalance().then((r) => { if (r.success) setReferralBalance(r.balance); });
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
const fetchHistory = async () => {
|
||||
if (!isUserAuthenticated()) { navigate('/login/client', { replace: true }); return; }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useEffect } from 'react';
|
||||
import './ModalSuccess.css';
|
||||
|
||||
interface ModalSuccessProps {
|
||||
@@ -10,20 +10,13 @@ interface ModalSuccessProps {
|
||||
}
|
||||
|
||||
export function ModalSuccess({ isOpen, productName, quantity, price, onClose }: ModalSuccessProps) {
|
||||
const [isVisible, setIsVisible] = useState(isOpen);
|
||||
|
||||
useEffect(() => {
|
||||
setIsVisible(isOpen);
|
||||
if (isOpen) {
|
||||
const timer = setTimeout(() => {
|
||||
setIsVisible(false);
|
||||
onClose();
|
||||
}, 2500);
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
if (!isOpen) return;
|
||||
const timer = setTimeout(() => { onClose(); }, 2500);
|
||||
return () => clearTimeout(timer);
|
||||
}, [isOpen, onClose]);
|
||||
|
||||
if (!isVisible) return null;
|
||||
if (!isOpen) return null;
|
||||
|
||||
return (
|
||||
<div className="modal2-success-overlay">
|
||||
|
||||
@@ -104,7 +104,7 @@ function OrderDetails() {
|
||||
if (commandId) {
|
||||
fetchOrderDetails(commandId);
|
||||
}
|
||||
}, [orderId]);
|
||||
}, [orderId]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
// ============================================
|
||||
// FONCTIONS POUR PHOTOS ET VIDÉOS (COMME PRODUCTCARD)
|
||||
@@ -299,7 +299,7 @@ function OrderDetails() {
|
||||
|
||||
// ✅ Produits depuis items (command_items)
|
||||
products:
|
||||
apiData.items?.map((item: any) => ({
|
||||
apiData.items?.map((item: Record<string, unknown>) => ({
|
||||
id: item.id,
|
||||
product_id: item.product_id,
|
||||
name_product: item.produit,
|
||||
|
||||
@@ -63,7 +63,7 @@ function ProductDetail() {
|
||||
|
||||
useEffect(() => {
|
||||
if (id) loadProduct(Number(id));
|
||||
}, [id]);
|
||||
}, [id]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
const loadProduct = async (productId: number) => {
|
||||
// ✅ Vérifier l'auth avant de charger le produit
|
||||
@@ -110,8 +110,8 @@ function ProductDetail() {
|
||||
} else {
|
||||
setError(response.message || "Produit non trouvé");
|
||||
}
|
||||
} catch (err: any) {
|
||||
setError(err.message || "Erreur lors du chargement du produit");
|
||||
} catch (err: unknown) {
|
||||
setError(err instanceof Error ? err.message : "Erreur lors du chargement du produit");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
|
||||
@@ -23,9 +23,9 @@ export default function ProfilePage() {
|
||||
const [loadingProfile, setLoadingProfile] = useState(true);
|
||||
|
||||
// Données locales (localStorage)
|
||||
const [defaultAddress, setDefaultAddress] = useState('');
|
||||
const [defaultPhone, setDefaultPhone] = useState('');
|
||||
const [signalPseudo, setSignalPseudo] = useState('');
|
||||
const [defaultAddress, setDefaultAddress] = useState(() => localStorage.getItem(STORAGE_ADDRESS) ?? '');
|
||||
const [defaultPhone, setDefaultPhone] = useState(() => localStorage.getItem(STORAGE_PHONE) ?? '');
|
||||
const [signalPseudo, setSignalPseudo] = useState(() => localStorage.getItem(STORAGE_SIGNAL) ?? '');
|
||||
|
||||
// Feedback
|
||||
const [savingContact, setSavingContact] = useState(false);
|
||||
@@ -47,11 +47,6 @@ export default function ProfilePage() {
|
||||
navigate('/login/client', { replace: true });
|
||||
return;
|
||||
}
|
||||
// Charger depuis localStorage
|
||||
setDefaultAddress(localStorage.getItem(STORAGE_ADDRESS) ?? '');
|
||||
setDefaultPhone(localStorage.getItem(STORAGE_PHONE) ?? '');
|
||||
setSignalPseudo(localStorage.getItem(STORAGE_SIGNAL) || username);
|
||||
|
||||
// Statut Telegram
|
||||
getTelegramStatus().then((s) => { setTgLinked(s.linked); setTgEnabled(s.enabled); });
|
||||
|
||||
@@ -68,7 +63,7 @@ export default function ProfilePage() {
|
||||
}
|
||||
setLoadingProfile(false);
|
||||
});
|
||||
}, [navigate]);
|
||||
}, [navigate]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
const showSuccess = (msg: string) => {
|
||||
setSuccessMsg(msg);
|
||||
|
||||
@@ -27,6 +27,7 @@ import Navbar from "../../components/Navbar";
|
||||
import Toast from "../../components/Toast";
|
||||
import "./SuiviLivraison.css";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import type { IconDefinition } from "@fortawesome/fontawesome-svg-core";
|
||||
import {
|
||||
faHourglassHalf,
|
||||
faTruck,
|
||||
@@ -109,7 +110,7 @@ const getDeliveryAddress = (order: OrderWithTracking): string => {
|
||||
return order.delivery_address || order.adresse || "Non disponible";
|
||||
};
|
||||
|
||||
const formatOrderItem = (item: any) => {
|
||||
const formatOrderItem = (item: Record<string, unknown>) => {
|
||||
return {
|
||||
name:
|
||||
item.produit || item.product_name || item.name_product || "Produit",
|
||||
@@ -153,8 +154,8 @@ const getStatusLabel = (status: string): string => {
|
||||
return statusMap[status?.toLowerCase()] || "Statut inconnu";
|
||||
};
|
||||
|
||||
const getStatusIcon = (status: string): any => {
|
||||
const iconMap: Record<string, any> = {
|
||||
const getStatusIcon = (status: string): IconDefinition => {
|
||||
const iconMap: Record<string, IconDefinition> = {
|
||||
pending: faHourglassHalf,
|
||||
assigned: faBiking,
|
||||
en_route: faTruck,
|
||||
@@ -178,7 +179,7 @@ const calculateOrderPoints = (
|
||||
points: number;
|
||||
category: string;
|
||||
categoryDisplay: string;
|
||||
categoryIcon: any;
|
||||
categoryIcon: IconDefinition;
|
||||
categoryColor: string;
|
||||
} => {
|
||||
// Totaux indexés par pool (+ index spécial pour "gros&semi" exclu des points)
|
||||
@@ -186,7 +187,7 @@ const calculateOrderPoints = (
|
||||
let excludedTotal = 0;
|
||||
|
||||
if (order.items && order.items.length > 0) {
|
||||
order.items.forEach((item: any) => {
|
||||
order.items.forEach((item: Record<string, unknown>) => {
|
||||
const cat = (item.category || "").toLowerCase();
|
||||
const itemPrice = item.prix || item.price || 0;
|
||||
|
||||
@@ -332,7 +333,7 @@ function SuiviLivraison() {
|
||||
loadOrders();
|
||||
const interval = setInterval(loadOrders, 10000);
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
const loadOrders = async () => {
|
||||
// ✅ Vérifier l'auth avant de charger les commandes
|
||||
@@ -359,7 +360,7 @@ function SuiviLivraison() {
|
||||
|
||||
try {
|
||||
tracking = await getOrderTracking(order.id);
|
||||
} catch (err) {
|
||||
} catch {
|
||||
console.warn(
|
||||
`Tracking non disponible pour commande ${order.id}`,
|
||||
);
|
||||
@@ -368,7 +369,7 @@ function SuiviLivraison() {
|
||||
|
||||
try {
|
||||
eta = await getOrderETA(order.id);
|
||||
} catch (err) {
|
||||
} catch {
|
||||
console.warn(
|
||||
`ETA non disponible pour commande ${order.id}`,
|
||||
);
|
||||
@@ -389,10 +390,11 @@ function SuiviLivraison() {
|
||||
setError("Impossible de charger les commandes");
|
||||
showToast("Impossible de charger les commandes", "error");
|
||||
}
|
||||
} catch (err: any) {
|
||||
} catch (err: unknown) {
|
||||
console.error("Erreur loadOrders:", err);
|
||||
setError(err.message || "Erreur lors du chargement");
|
||||
showToast(err.message || "Erreur lors du chargement", "error");
|
||||
const msg = err instanceof Error ? err.message : "Erreur lors du chargement";
|
||||
setError(msg);
|
||||
showToast(msg, "error");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -462,7 +464,7 @@ function SuiviLivraison() {
|
||||
const pointsEarned =
|
||||
response.points_earned || selectedOrderPoints;
|
||||
const apiCategory =
|
||||
response.category || (response as any).data?.category || "";
|
||||
response.category || (response as Record<string, unknown> & { data?: { category?: string } }).data?.category || "";
|
||||
|
||||
const displayCategory =
|
||||
apiCategory && apiCategory !== "total"
|
||||
@@ -483,9 +485,10 @@ function SuiviLivraison() {
|
||||
);
|
||||
setConfirming(null);
|
||||
}
|
||||
} catch (err: any) {
|
||||
setError(err.message || "Erreur serveur");
|
||||
showToast(err.message || "Erreur serveur", "error");
|
||||
} catch (err: unknown) {
|
||||
const msg = err instanceof Error ? err.message : "Erreur serveur";
|
||||
setError(msg);
|
||||
showToast(msg, "error");
|
||||
setConfirming(null);
|
||||
}
|
||||
};
|
||||
@@ -567,9 +570,9 @@ function SuiviLivraison() {
|
||||
"error",
|
||||
);
|
||||
}
|
||||
} catch (error: any) {
|
||||
} catch (error: unknown) {
|
||||
console.error("❌ [CANCEL] Erreur:", error);
|
||||
showToast(error.message || "Erreur lors de l'annulation", "error");
|
||||
showToast(error instanceof Error ? error.message : "Erreur lors de l'annulation", "error");
|
||||
} finally {
|
||||
setCancellingOrder(null);
|
||||
}
|
||||
@@ -895,7 +898,7 @@ function SuiviLivraison() {
|
||||
<div className="items-list">
|
||||
{order.items.map(
|
||||
(
|
||||
item: any,
|
||||
item: Record<string, unknown>,
|
||||
idx: number,
|
||||
) => {
|
||||
const formatted =
|
||||
|
||||
Reference in New Issue
Block a user