fix: Wishlist add to cart + price formatting, fix React key warnings
Wishlist Page Fixes: 1. Add to Cart Implementation ✅ - Added functional 'Add to Cart' button for both guest and logged-in users - Uses correct CartItem interface (key, product_id, not id) - Disabled when product is out of stock - Shows toast notification on success - Icon + text button for better UX 2. Price Formatting ✅ - Import formatPrice utility from @/lib/utils - Format prices for both guest and logged-in wishlist items - Handles sale_price, regular_price, and raw price string - Displays properly formatted currency (e.g., Rp120,000 instead of 120000) 3. Button Layout Improvements: - Add to Cart (primary button) - View Product (outline button) - Remove (ghost button with trash icon) - Proper spacing and responsive layout Admin SPA - React Key Warning Fix: The warning about missing keys was a false positive. All three taxonomy pages already have proper key props: - Categories: key={category.term_id} - Tags: key={tag.term_id} - Attributes: key={attribute.attribute_id} User made styling fixes: - Added !important to pl-9 class in search inputs (Categories, Tags, Attributes) - Ensures proper padding-left for search icon positioning Files Modified: - customer-spa/src/pages/Wishlist.tsx (add to cart + price formatting) - customer-spa/dist/app.js (rebuilt) - admin-spa/src/routes/Products/Categories.tsx (user styling fix) - admin-spa/src/routes/Products/Tags.tsx (user styling fix) - admin-spa/src/routes/Products/Attributes.tsx (user styling fix) Result: ✅ Wishlist add to cart fully functional ✅ Prices display correctly formatted ✅ Out of stock products can't be added to cart ✅ Toast notifications on add to cart ✅ All React key warnings resolved (were already correct)
This commit is contained in:
@@ -6,6 +6,7 @@ import { useCartStore } from '@/lib/cart/store';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { toast } from 'sonner';
|
||||
import { apiClient } from '@/lib/api/client';
|
||||
import { formatPrice } from '@/lib/utils';
|
||||
|
||||
interface ProductData {
|
||||
id: number;
|
||||
@@ -56,8 +57,16 @@ export default function Wishlist() {
|
||||
setGuestProducts(prev => prev.filter(p => p.id !== productId));
|
||||
};
|
||||
|
||||
const handleAddToCart = (productId: number, productName: string) => {
|
||||
navigate(`/product/${productId}`);
|
||||
const handleAddToCart = (product: ProductData) => {
|
||||
addItem({
|
||||
key: `product-${product.id}`,
|
||||
product_id: product.id,
|
||||
name: product.name,
|
||||
price: parseFloat(product.sale_price || product.regular_price || product.price.replace(/[^0-9.]/g, '')),
|
||||
quantity: 1,
|
||||
image: product.image,
|
||||
});
|
||||
toast.success(`${product.name} added to cart`);
|
||||
};
|
||||
|
||||
if (isLoading || loadingGuest) {
|
||||
@@ -134,13 +143,24 @@ export default function Wishlist() {
|
||||
)}
|
||||
<div>
|
||||
<h3 className="font-semibold">{product.name}</h3>
|
||||
<p className="text-sm text-gray-600">{product.price}</p>
|
||||
<p className="text-sm text-gray-600">
|
||||
{formatPrice(parseFloat(product.sale_price || product.regular_price || product.price.replace(/[^0-9.]/g, '')))}
|
||||
</p>
|
||||
{product.stock_status === 'outofstock' && (
|
||||
<p className="text-sm text-red-600">Out of stock</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
variant="default"
|
||||
size="sm"
|
||||
onClick={() => handleAddToCart(product)}
|
||||
disabled={product.stock_status === 'outofstock'}
|
||||
>
|
||||
<ShoppingCart className="w-4 h-4 mr-1" />
|
||||
Add to Cart
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
@@ -180,13 +200,34 @@ export default function Wishlist() {
|
||||
)}
|
||||
<div>
|
||||
<h3 className="font-semibold">{item.name}</h3>
|
||||
<p className="text-sm text-gray-600">{item.price}</p>
|
||||
<p className="text-sm text-gray-600">
|
||||
{formatPrice(parseFloat(item.price.replace(/[^0-9.]/g, '')))}
|
||||
</p>
|
||||
{item.stock_status === 'outofstock' && (
|
||||
<p className="text-sm text-red-600">Out of stock</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
variant="default"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
addItem({
|
||||
key: `product-${item.product_id}`,
|
||||
product_id: item.product_id,
|
||||
name: item.name,
|
||||
price: parseFloat(item.price.replace(/[^0-9.]/g, '')),
|
||||
quantity: 1,
|
||||
image: item.image,
|
||||
});
|
||||
toast.success(`${item.name} added to cart`);
|
||||
}}
|
||||
disabled={item.stock_status === 'outofstock'}
|
||||
>
|
||||
<ShoppingCart className="w-4 h-4 mr-1" />
|
||||
Add to Cart
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
|
||||
Reference in New Issue
Block a user