diff --git a/backend/gestion/db/db_product.go b/backend/gestion/db/db_product.go index d1fa452b..46902129 100644 --- a/backend/gestion/db/db_product.go +++ b/backend/gestion/db/db_product.go @@ -69,8 +69,8 @@ func (d *Database) CreateProduct(product any) error { p.SetUpdatedAt(result.UpdatedAt) for i, price := range p.GetPrices() { - err := d.GDB.Exec(`INSERT INTO product_prices (product_id, quantity, price) VALUES (?, ?, ?)`, - result.ID, price.Quantity, price.Price).Error + err := d.GDB.Exec(`INSERT INTO product_prices (product_id, quantity, price, active_price) VALUES (?, ?, ?, ?)`, + result.ID, price.Quantity, price.Price, price.ActivePrice).Error if err != nil { log.Printf("❌ [DB CreateProduct] Erreur insertion prix[%d]: %v", i, err) return fmt.Errorf("erreur insertion prix: %v", err) @@ -190,8 +190,8 @@ func (d *Database) UpdateProduct(productID int, name, category, description, uni d.GDB.Exec(`DELETE FROM product_prices WHERE product_id = ?`, productID) for _, price := range prices { - if err := d.GDB.Exec(`INSERT INTO product_prices (product_id, quantity, price) VALUES (?, ?, ?)`, - productID, price.Quantity, price.Price).Error; err != nil { + if err := d.GDB.Exec(`INSERT INTO product_prices (product_id, quantity, price, active_price) VALUES (?, ?, ?, ?)`, + productID, price.Quantity, price.Price, price.ActivePrice).Error; err != nil { log.Printf("❌ [UpdateProduct] Erreur prix: %v", err) } } diff --git a/frontend-admin/src/api/api_admin.ts b/frontend-admin/src/api/api_admin.ts index ebe26110..697e3b48 100644 --- a/frontend-admin/src/api/api_admin.ts +++ b/frontend-admin/src/api/api_admin.ts @@ -747,6 +747,20 @@ export const deleteProductMediaAdmin = async ( return { success: true, message: data.message }; }; +export const activateProductPrice = async (priceId: number) => { + const { data } = await apiClient.post( + `${V2}/admin/protected/active/product/price/${priceId}`, + ); + return { success: true, message: data.message }; +}; + +export const deactivateProductPrice = async (priceId: number) => { + const { data } = await apiClient.post( + `${V2}/admin/protected/desactive/product/price/${priceId}`, + ); + return { success: true, message: data.message }; +}; + // ============================================ // ADDRESSES // ============================================ diff --git a/frontend-admin/src/api/types.ts b/frontend-admin/src/api/types.ts index bd82bb33..8de7bb33 100644 --- a/frontend-admin/src/api/types.ts +++ b/frontend-admin/src/api/types.ts @@ -102,7 +102,7 @@ export interface Product { category: string; stock: number; unit: string; - prices?: Array<{ quantity: number; price: number }>; + prices?: Array<{ id?: number; quantity: number; price: number; active_price?: boolean }>; media?: Array<{ url: string; type: string; id?: number; created_at?: string }>; created_at?: string; updated_at?: string; diff --git a/frontend-admin/src/screens/admin/ProductsScreen.tsx b/frontend-admin/src/screens/admin/ProductsScreen.tsx index f9a2944b..7da6b72f 100644 --- a/frontend-admin/src/screens/admin/ProductsScreen.tsx +++ b/frontend-admin/src/screens/admin/ProductsScreen.tsx @@ -26,6 +26,8 @@ import { deleteProductAdmin, uploadProductMediaAdmin, deleteProductMediaAdmin, + activateProductPrice, + deactivateProductPrice, getCategories, } from "../../api/api_admin"; import type { Category } from "../../api/api_admin"; @@ -40,8 +42,10 @@ import { useAlert } from "../../hooks/useAlert"; // Types // -------------------------------------------------- interface PriceRow { + id?: number; quantity: string; price: string; + active: boolean; } interface MediaItem { id?: number; @@ -82,7 +86,7 @@ const emptyForm = (firstCategory = ""): FormState => ({ description: "", stock: "", unit: "u", - prices: [{ quantity: "1", price: "" }], + prices: [{ quantity: "1", price: "", active: true }], }); // ================================================== @@ -162,10 +166,12 @@ export default function ProductsScreen() { prices: product.prices && product.prices.length > 0 ? product.prices.map((p) => ({ + id: p.id, quantity: p.quantity.toString(), price: p.price.toString(), + active: p.active_price ?? true, })) - : [{ quantity: "1", price: "" }], + : [{ quantity: "1", price: "", active: true }], }); setExistingMedia( (product.media || []).map((m) => ({ @@ -191,8 +197,15 @@ export default function ProductsScreen() { const addPriceRow = () => setForm((f) => ({ ...f, - prices: [...f.prices, { quantity: "1", price: "" }], + prices: [...f.prices, { quantity: "1", price: "", active: true }], })); + + const togglePriceActive = (idx: number) => + setForm((f) => { + const prices = [...f.prices]; + prices[idx] = { ...prices[idx], active: !prices[idx].active }; + return { ...f, prices }; + }); const removePriceRow = (idx: number) => setForm((f) => ({ ...f, @@ -292,6 +305,7 @@ export default function ProductsScreen() { const prices = form.prices.map((p) => ({ quantity: parseFloat(p.quantity), price: parseFloat(p.price), + active_price: p.active, })); try { @@ -329,11 +343,10 @@ export default function ProductsScreen() { fd.append("stock", form.stock); fd.append("unit", form.unit); - // ✅ FIX PRINCIPAL : Envoyer les prix au format que Go attend - // Backend attend : prices[0][quantity], prices[0][price], etc. prices.forEach((p, i) => { fd.append(`prices[${i}][quantity]`, String(p.quantity)); fd.append(`prices[${i}][price]`, String(p.price)); + fd.append(`prices[${i}][active_price]`, p.active_price ? "true" : "false"); }); // Attacher les médias en attente @@ -647,6 +660,7 @@ export default function ProductsScreen() { paddingHorizontal: spacing.m, }, removePriceBtn: { padding: spacing.s, marginBottom: 4 }, + toggleActiveBtn: { padding: spacing.s, marginBottom: 4 }, // Media section mediaSectionBox: { marginTop: spacing.m }, @@ -803,11 +817,24 @@ export default function ProductsScreen() { )} Stock: {item.stock} {item.unit || "u"} {item.prices && item.prices.length > 0 && ( - - {item.prices - .map((p) => `${p.quantity}${item.unit || "u"} = ${p.price}€`) - .join(" | ")} - + + {item.prices.map((p, i) => ( + + {p.quantity}{item.unit || "u"} = {p.price}€ + {i < item.prices!.length - 1 ? " |" : ""} + + ))} + )} {item.description && ( @@ -1055,6 +1082,16 @@ export default function ProductsScreen() { } /> + togglePriceActive(idx)} + > + + {form.prices.length > 1 && (