/**
* @file Products.js
* @description Écran catalogue listant l'ensemble des produits disponibles.
* Récupère les produits depuis l'API au montage du composant et les affiche
* sous forme de grille à deux colonnes. Chaque produit est cliquable et
* redirige vers la fiche détaillée (ProductsCard).
*/
import React, { useEffect, useState } from 'react';
import { FlatList, Text, TouchableOpacity, View, Image, ActivityIndicator, StyleSheet } from 'react-native';
import { useNavigation } from "@react-navigation/native";
import ProtectedRoute from "../components/ProtectedRoute";
import { GlobalStyles } from "../styles/GlobalStyles";
import {EXPO_PUBLIC_API_URL} from "../config";
/**
* Écran affichant le catalogue complet des produits sous forme de grille.
*
* @component
* @returns {React.JSX.Element} La grille des produits, ou un état de chargement/erreur/vide.
*/
const Products = () => {
/** @type {[Array<Object>, Function]} Liste des produits récupérés depuis l'API. */
const [data, setData] = useState([]);
/** @type {[boolean, Function]} Indicateur de chargement des données. */
const [loading, setLoading] = useState(true);
/** @type {[string|null, Function]} Message d'erreur en cas d'échec du fetch. */
const [error, setError] = useState(null);
const navigation = useNavigation();
useEffect(() => {
const abortController = new AbortController();
/**
* Récupère la liste complète des produits depuis l'API.
* Gère les états de chargement et d'erreur.
*
* @async
* @returns {Promise<void>}
*/
const fetchProductsData = async () => {
setLoading(true);
setError(null);
try {
const response = await fetch (
`${EXPO_PUBLIC_API_URL}/produits/`,
{ signal: abortController.signal }
);
const productsList = await response.json();
if (!response.ok) {
throw new Error(productsList.message || "Erreur lors de la récupération des produits.");
}
setData(productsList);
} catch(error) {
if (error.name !== 'AbortError') {
console.error("Erreur de fetch produits:", error);
setError("Impossible de charger les produits. Veuillez réessayer.");
}
} finally {
setLoading(false);
}
};
fetchProductsData();
return () => abortController.abort();
}, []);
if (loading) {
return (
<View style={GlobalStyles.loadingContainer}>
<ActivityIndicator size="large" color="#1e3c72" />
<Text style={GlobalStyles.loadingText}>Chargement des produits...</Text>
</View>
);
}
if (error) {
return (
<View style={GlobalStyles.container}>
<Text style={GlobalStyles.errorText}>{error}</Text>
</View>
);
}
if (data.length === 0) {
return (
<View style={GlobalStyles.container}>
<Text style={GlobalStyles.emptyText}>Aucun produit disponible pour le moment.</Text>
</View>
);
}
return (
<FlatList
data={data}
keyExtractor={(item) => item.reference.toString()}
numColumns={2}
contentContainerStyle={GlobalStyles.productsGridContainer}
renderItem={({ item }) => (
<TouchableOpacity
style={GlobalStyles.productGridItem}
onPress={() => navigation.navigate("ProductsCard", item)}
>
<Image
source={{ uri:`${EXPO_PUBLIC_API_URL}` + "/images/produits/" + item.imageUrl }}
style={GlobalStyles.productImage}
/>
<Text style={GlobalStyles.productName}>{item.designation}</Text>
<Text style={GlobalStyles.productName}>{item.prix_unitaire_HT} €</Text>
</TouchableOpacity>
)}
/>
);
};
export default Products;