feat: active & deactivate the price for product
This commit is contained in:
@@ -69,8 +69,8 @@ func (d *Database) CreateProduct(product any) error {
|
|||||||
p.SetUpdatedAt(result.UpdatedAt)
|
p.SetUpdatedAt(result.UpdatedAt)
|
||||||
|
|
||||||
for i, price := range p.GetPrices() {
|
for i, price := range p.GetPrices() {
|
||||||
err := d.GDB.Exec(`INSERT INTO product_prices (product_id, quantity, price) VALUES (?, ?, ?)`,
|
err := d.GDB.Exec(`INSERT INTO product_prices (product_id, quantity, price, active_price) VALUES (?, ?, ?, ?)`,
|
||||||
result.ID, price.Quantity, price.Price).Error
|
result.ID, price.Quantity, price.Price, price.ActivePrice).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("❌ [DB CreateProduct] Erreur insertion prix[%d]: %v", i, err)
|
log.Printf("❌ [DB CreateProduct] Erreur insertion prix[%d]: %v", i, err)
|
||||||
return fmt.Errorf("erreur insertion prix: %v", 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)
|
d.GDB.Exec(`DELETE FROM product_prices WHERE product_id = ?`, productID)
|
||||||
|
|
||||||
for _, price := range prices {
|
for _, price := range prices {
|
||||||
if err := d.GDB.Exec(`INSERT INTO product_prices (product_id, quantity, price) VALUES (?, ?, ?)`,
|
if err := d.GDB.Exec(`INSERT INTO product_prices (product_id, quantity, price, active_price) VALUES (?, ?, ?, ?)`,
|
||||||
productID, price.Quantity, price.Price).Error; err != nil {
|
productID, price.Quantity, price.Price, price.ActivePrice).Error; err != nil {
|
||||||
log.Printf("❌ [UpdateProduct] Erreur prix: %v", err)
|
log.Printf("❌ [UpdateProduct] Erreur prix: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -747,6 +747,20 @@ export const deleteProductMediaAdmin = async (
|
|||||||
return { success: true, message: data.message };
|
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
|
// ADDRESSES
|
||||||
// ============================================
|
// ============================================
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ export interface Product {
|
|||||||
category: string;
|
category: string;
|
||||||
stock: number;
|
stock: number;
|
||||||
unit: string;
|
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 }>;
|
media?: Array<{ url: string; type: string; id?: number; created_at?: string }>;
|
||||||
created_at?: string;
|
created_at?: string;
|
||||||
updated_at?: string;
|
updated_at?: string;
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ import {
|
|||||||
deleteProductAdmin,
|
deleteProductAdmin,
|
||||||
uploadProductMediaAdmin,
|
uploadProductMediaAdmin,
|
||||||
deleteProductMediaAdmin,
|
deleteProductMediaAdmin,
|
||||||
|
activateProductPrice,
|
||||||
|
deactivateProductPrice,
|
||||||
getCategories,
|
getCategories,
|
||||||
} from "../../api/api_admin";
|
} from "../../api/api_admin";
|
||||||
import type { Category } from "../../api/api_admin";
|
import type { Category } from "../../api/api_admin";
|
||||||
@@ -40,8 +42,10 @@ import { useAlert } from "../../hooks/useAlert";
|
|||||||
// Types
|
// Types
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
interface PriceRow {
|
interface PriceRow {
|
||||||
|
id?: number;
|
||||||
quantity: string;
|
quantity: string;
|
||||||
price: string;
|
price: string;
|
||||||
|
active: boolean;
|
||||||
}
|
}
|
||||||
interface MediaItem {
|
interface MediaItem {
|
||||||
id?: number;
|
id?: number;
|
||||||
@@ -82,7 +86,7 @@ const emptyForm = (firstCategory = ""): FormState => ({
|
|||||||
description: "",
|
description: "",
|
||||||
stock: "",
|
stock: "",
|
||||||
unit: "u",
|
unit: "u",
|
||||||
prices: [{ quantity: "1", price: "" }],
|
prices: [{ quantity: "1", price: "", active: true }],
|
||||||
});
|
});
|
||||||
|
|
||||||
// ==================================================
|
// ==================================================
|
||||||
@@ -162,10 +166,12 @@ export default function ProductsScreen() {
|
|||||||
prices:
|
prices:
|
||||||
product.prices && product.prices.length > 0
|
product.prices && product.prices.length > 0
|
||||||
? product.prices.map((p) => ({
|
? product.prices.map((p) => ({
|
||||||
|
id: p.id,
|
||||||
quantity: p.quantity.toString(),
|
quantity: p.quantity.toString(),
|
||||||
price: p.price.toString(),
|
price: p.price.toString(),
|
||||||
|
active: p.active_price ?? true,
|
||||||
}))
|
}))
|
||||||
: [{ quantity: "1", price: "" }],
|
: [{ quantity: "1", price: "", active: true }],
|
||||||
});
|
});
|
||||||
setExistingMedia(
|
setExistingMedia(
|
||||||
(product.media || []).map((m) => ({
|
(product.media || []).map((m) => ({
|
||||||
@@ -191,8 +197,15 @@ export default function ProductsScreen() {
|
|||||||
const addPriceRow = () =>
|
const addPriceRow = () =>
|
||||||
setForm((f) => ({
|
setForm((f) => ({
|
||||||
...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) =>
|
const removePriceRow = (idx: number) =>
|
||||||
setForm((f) => ({
|
setForm((f) => ({
|
||||||
...f,
|
...f,
|
||||||
@@ -292,6 +305,7 @@ export default function ProductsScreen() {
|
|||||||
const prices = form.prices.map((p) => ({
|
const prices = form.prices.map((p) => ({
|
||||||
quantity: parseFloat(p.quantity),
|
quantity: parseFloat(p.quantity),
|
||||||
price: parseFloat(p.price),
|
price: parseFloat(p.price),
|
||||||
|
active_price: p.active,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -329,11 +343,10 @@ export default function ProductsScreen() {
|
|||||||
fd.append("stock", form.stock);
|
fd.append("stock", form.stock);
|
||||||
fd.append("unit", form.unit);
|
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) => {
|
prices.forEach((p, i) => {
|
||||||
fd.append(`prices[${i}][quantity]`, String(p.quantity));
|
fd.append(`prices[${i}][quantity]`, String(p.quantity));
|
||||||
fd.append(`prices[${i}][price]`, String(p.price));
|
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
|
// Attacher les médias en attente
|
||||||
@@ -647,6 +660,7 @@ export default function ProductsScreen() {
|
|||||||
paddingHorizontal: spacing.m,
|
paddingHorizontal: spacing.m,
|
||||||
},
|
},
|
||||||
removePriceBtn: { padding: spacing.s, marginBottom: 4 },
|
removePriceBtn: { padding: spacing.s, marginBottom: 4 },
|
||||||
|
toggleActiveBtn: { padding: spacing.s, marginBottom: 4 },
|
||||||
|
|
||||||
// Media section
|
// Media section
|
||||||
mediaSectionBox: { marginTop: spacing.m },
|
mediaSectionBox: { marginTop: spacing.m },
|
||||||
@@ -803,11 +817,24 @@ export default function ProductsScreen() {
|
|||||||
)}
|
)}
|
||||||
<Text style={styles.info}>Stock: {item.stock} {item.unit || "u"}</Text>
|
<Text style={styles.info}>Stock: {item.stock} {item.unit || "u"}</Text>
|
||||||
{item.prices && item.prices.length > 0 && (
|
{item.prices && item.prices.length > 0 && (
|
||||||
<Text style={styles.info}>
|
<View style={{ flexDirection: "row", flexWrap: "wrap", gap: 4, marginTop: 2 }}>
|
||||||
{item.prices
|
{item.prices.map((p, i) => (
|
||||||
.map((p) => `${p.quantity}${item.unit || "u"} = ${p.price}€`)
|
<Text
|
||||||
.join(" | ")}
|
key={i}
|
||||||
</Text>
|
style={[
|
||||||
|
styles.info,
|
||||||
|
!p.active_price && {
|
||||||
|
textDecorationLine: "line-through",
|
||||||
|
color: colors.textMuted,
|
||||||
|
opacity: 0.5,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{p.quantity}{item.unit || "u"} = {p.price}€
|
||||||
|
{i < item.prices!.length - 1 ? " |" : ""}
|
||||||
|
</Text>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
)}
|
)}
|
||||||
{item.description && (
|
{item.description && (
|
||||||
<Text style={styles.desc} numberOfLines={2}>
|
<Text style={styles.desc} numberOfLines={2}>
|
||||||
@@ -1055,6 +1082,16 @@ export default function ProductsScreen() {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={styles.toggleActiveBtn}
|
||||||
|
onPress={() => togglePriceActive(idx)}
|
||||||
|
>
|
||||||
|
<Ionicons
|
||||||
|
name={p.active ? "checkmark-circle" : "close-circle"}
|
||||||
|
size={22}
|
||||||
|
color={p.active ? colors.success || "#22c55e" : colors.danger}
|
||||||
|
/>
|
||||||
|
</TouchableOpacity>
|
||||||
{form.prices.length > 1 && (
|
{form.prices.length > 1 && (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={styles.removePriceBtn}
|
style={styles.removePriceBtn}
|
||||||
|
|||||||
Reference in New Issue
Block a user