From 04671637d229199d48f98eb807278ef348e12a6d Mon Sep 17 00:00:00 2001 From: Xor290 Date: Fri, 15 May 2026 16:45:56 +0200 Subject: [PATCH] update --- backend/gestion/handlers/product.go | 51 ++++++++++++++++--- frontend-prep/src/api/api_types.ts | 1 + frontend-prep/src/pages/User/Accueil.tsx | 9 ++-- .../src/pages/User/ProductDetail.tsx | 15 +++--- mobile/app.json | 2 +- mobile/src/api/api_types.ts | 1 + mobile/src/components/ProductCard.tsx | 5 +- .../screens/client/ProductDetailScreen.tsx | 11 ++-- 8 files changed, 70 insertions(+), 25 deletions(-) diff --git a/backend/gestion/handlers/product.go b/backend/gestion/handlers/product.go index ea504405..a169d819 100644 --- a/backend/gestion/handlers/product.go +++ b/backend/gestion/handlers/product.go @@ -456,7 +456,7 @@ func GetAllProducts(c *gin.Context) { }) return } - + products = filterActivePrices(products) c.JSON(http.StatusOK, gin.H{ "success": true, "data": products, @@ -493,17 +493,16 @@ func GetProductsByCategory(c *gin.Context) { media, _ := database.GetMediaByProductID(products[i].ID) products[i].Media = media } - + filteredProducts := filterActivePrices(products) c.JSON(http.StatusOK, gin.H{ "success": true, - "data": products, - "count": len(products), + "data": filteredProducts, + "count": len(filteredProducts), }) } func GetProductByID(c *gin.Context) { database := c.MustGet("database").(*db.Database) - id, err := strconv.Atoi(c.Param("id")) if err != nil || id <= 0 { c.JSON(http.StatusBadRequest, gin.H{ @@ -512,7 +511,6 @@ func GetProductByID(c *gin.Context) { }) return } - product, err := database.GetProductByID(id) if err != nil { c.JSON(http.StatusNotFound, gin.H{ @@ -521,11 +519,13 @@ func GetProductByID(c *gin.Context) { }) return } - // ✅ Charger les médias media, _ := database.GetMediaByProductID(product.ID) product.Media = media + // ✅ Filtrer les prix désactivés + filterActivepricesSingle(&product) + c.JSON(http.StatusOK, gin.H{ "success": true, "data": product, @@ -563,6 +563,7 @@ func UpdateProduct(c *gin.Context) { Description string `json:"description"` Unit string `json:"unit"` Prices []models.ProductPrice `json:"prices"` + Stock *float64 `json:"stock"` } if err := c.ShouldBindJSON(&updateData); err != nil { @@ -607,6 +608,13 @@ func UpdateProduct(c *gin.Context) { } } + if updateData.Stock != nil { + if err := validateStock(*updateData.Stock); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + } + log.Printf("🔄 [UpdateProduct] %s met à jour produit #%d", username, id) if err := database.UpdateProduct(id, updateData.Name, updateData.Category, updateData.Description, updateData.Unit, updateData.Prices); err != nil { @@ -615,6 +623,12 @@ func UpdateProduct(c *gin.Context) { return } + if updateData.Stock != nil { + if err := database.SetProductStock(id, *updateData.Stock); err != nil { + log.Printf("⚠️ [UpdateProduct] Erreur mise à jour stock: %v", err) + } + } + // ✅ RÉCUPÉRER LE PRODUIT MIS À JOUR updatedProduct, _ := database.GetProductByID(id) media, _ := database.GetMediaByProductID(id) @@ -1013,3 +1027,26 @@ func cleanFileName(name string) string { return result } + +func filterActivePrices(products []models.Product) []models.Product { + for i := range products { + activePrices := []models.ProductPrice{} + for _, p := range products[i].Prices { + if p.ActivePrice { + activePrices = append(activePrices, p) + } + } + products[i].Prices = activePrices + } + return products +} + +func filterActivepricesSingle(product *models.Product) { + activePrices := []models.ProductPrice{} + for _, p := range product.Prices { + if p.ActivePrice { + activePrices = append(activePrices, p) + } + } + product.Prices = activePrices +} diff --git a/frontend-prep/src/api/api_types.ts b/frontend-prep/src/api/api_types.ts index fac2599f..1ed559af 100644 --- a/frontend-prep/src/api/api_types.ts +++ b/frontend-prep/src/api/api_types.ts @@ -366,6 +366,7 @@ export interface TrackingResponse { export interface ProductPrice { quantity: number; price: number; + active_price?: boolean; } export interface Product { id: number; diff --git a/frontend-prep/src/pages/User/Accueil.tsx b/frontend-prep/src/pages/User/Accueil.tsx index 6be4e40b..c79ce427 100644 --- a/frontend-prep/src/pages/User/Accueil.tsx +++ b/frontend-prep/src/pages/User/Accueil.tsx @@ -70,10 +70,9 @@ function UserAccueil() { }; const getProductPrice = (product: Product): number => { - if (!product.prices || product.prices.length === 0) { - return 0; - } - return product.prices[0]?.price || 0; + const activePrices = product.prices?.filter(p => p.active_price !== false); + if (!activePrices || activePrices.length === 0) return 0; + return activePrices[0].price; }; const hasProductVideo = (product: Product): boolean => { if (!product.media || product.media.length === 0) { @@ -210,7 +209,7 @@ function UserAccueil() { image={getProductImage(product)} stock={product.stock} category={product.category} - prices={product.prices} + prices={product.prices?.filter(p => p.active_price !== false)} hasVideo={hasProductVideo(product)} videoUrl={getProductVideoUrl(product)} categoryColor={ diff --git a/frontend-prep/src/pages/User/ProductDetail.tsx b/frontend-prep/src/pages/User/ProductDetail.tsx index 328faec7..9f095e08 100644 --- a/frontend-prep/src/pages/User/ProductDetail.tsx +++ b/frontend-prep/src/pages/User/ProductDetail.tsx @@ -86,12 +86,15 @@ function ProductDetail() { const fixedProduct = { ...response.data, prices: - response.data.prices?.map( - (p: { quantity: number; price: number }) => ({ - quantity: parseFloat(String(p.quantity)), - price: parseFloat(String(p.price)), - }), - ) || [], + response.data.prices + ?.filter((p: { quantity: number; price: number; active_price?: boolean }) => p.active_price !== false) + .map( + (p: { quantity: number; price: number; active_price?: boolean }) => ({ + quantity: parseFloat(String(p.quantity)), + price: parseFloat(String(p.price)), + active_price: p.active_price, + }), + ) || [], }; setProduct(fixedProduct); diff --git a/mobile/app.json b/mobile/app.json index b5734121..33c634de 100644 --- a/mobile/app.json +++ b/mobile/app.json @@ -2,7 +2,7 @@ "expo": { "name": "Milieu Nantais", "slug": "frontend-client", - "version": "1.0.1", + "version": "1.0.2", "orientation": "portrait", "icon": "./assets/icon.png", "userInterfaceStyle": "dark", diff --git a/mobile/src/api/api_types.ts b/mobile/src/api/api_types.ts index cc0be561..2fa73144 100644 --- a/mobile/src/api/api_types.ts +++ b/mobile/src/api/api_types.ts @@ -356,6 +356,7 @@ export interface TrackingResponse { export interface ProductPrice { quantity: number; price: number; + active_price?: boolean; } export interface Product { id: number; diff --git a/mobile/src/components/ProductCard.tsx b/mobile/src/components/ProductCard.tsx index 158d2435..1f34cd5d 100644 --- a/mobile/src/components/ProductCard.tsx +++ b/mobile/src/components/ProductCard.tsx @@ -48,7 +48,8 @@ export default function ProductCard({ product, onPress, categoryColor }: Product const { addToCart } = useCart(); const catColor = categoryColor ?? colors.accent; const isSoldOut = product.stock <= 0; - const firstPrice = product.prices?.[0]?.price ?? null; + const activePrices = product.prices?.filter((p) => p.active_price !== false) ?? []; + const firstPrice = activePrices[0]?.price ?? null; const imageMedia = product.media?.find((m) => m.type === "image"); const videoMedia = product.media?.find((m) => m.type === "video"); @@ -267,7 +268,7 @@ export default function ProductCard({ product, onPress, categoryColor }: Product style={styles.pickerScroll} showsVerticalScrollIndicator={false} > - {product.prices?.map((p) => ( + {activePrices.map((p) => ( ({ - quantity: parseFloat(String(pr.quantity)), - price: parseFloat(String(pr.price)), - })) || [], + p.prices + ?.filter((pr: any) => pr.active_price !== false) + .map((pr: any) => ({ + quantity: parseFloat(String(pr.quantity)), + price: parseFloat(String(pr.price)), + active_price: pr.active_price, + })) || [], }; setProduct(fixedProduct); if (fixedProduct.prices.length > 0) {