redesing product lists

This commit is contained in:
2025-05-28 11:01:42 +02:00
parent 330124837f
commit eb3ae05425
12 changed files with 1569 additions and 809 deletions

View File

@@ -35,7 +35,8 @@ const AddShoppingEventModal: React.FC<AddShoppingEventModalProps> = ({
const [newProductItem, setNewProductItem] = useState<ProductInEvent>({
product_id: 0,
amount: 1,
price: 0
price: 0,
discount: false
});
const [autoCalculate, setAutoCalculate] = useState<boolean>(true);
@@ -58,7 +59,8 @@ const AddShoppingEventModal: React.FC<AddShoppingEventModalProps> = ({
const mappedProducts = editEvent.products.map(p => ({
product_id: p.id,
amount: p.amount,
price: p.price
price: p.price,
discount: p.discount
}));
// Calculate the sum of all products
@@ -220,7 +222,7 @@ const AddShoppingEventModal: React.FC<AddShoppingEventModalProps> = ({
const addProductToEvent = () => {
if (newProductItem.product_id > 0 && newProductItem.amount > 0 && newProductItem.price >= 0) {
setSelectedProducts([...selectedProducts, { ...newProductItem }]);
setNewProductItem({ product_id: 0, amount: 1, price: 0 });
setNewProductItem({ product_id: 0, amount: 1, price: 0, discount: false });
}
};
@@ -234,7 +236,8 @@ const AddShoppingEventModal: React.FC<AddShoppingEventModalProps> = ({
setNewProductItem({
product_id: productToEdit.product_id,
amount: productToEdit.amount,
price: productToEdit.price
price: productToEdit.price,
discount: productToEdit.discount
});
// Remove the item from the selected list
setSelectedProducts(selectedProducts.filter((_, i) => i !== index));
@@ -246,7 +249,8 @@ const AddShoppingEventModal: React.FC<AddShoppingEventModalProps> = ({
const weightInfo = product.weight ? `${product.weight}${product.weight_unit}` : product.weight_unit;
const organicEmoji = product.organic ? ' 🌱' : '';
return `${product.name}${organicEmoji} ${weightInfo}`;
const brandInfo = product.brand ? ` (${product.brand.name})` : '';
return `${product.name}${organicEmoji} ${weightInfo}${brandInfo}`;
};
// Filter products based on selected shop's brands
@@ -297,38 +301,38 @@ const AddShoppingEventModal: React.FC<AddShoppingEventModalProps> = ({
)}
<form onSubmit={handleSubmit} className="space-y-6">
{/* Shop Selection */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Shop
</label>
<select
value={formData.shop_id}
onChange={(e) => setFormData({...formData, shop_id: parseInt(e.target.value)})}
className="w-full border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
required
>
<option value={0}>Select a shop</option>
{shops.map(shop => (
<option key={shop.id} value={shop.id}>
{shop.name} - {shop.city}
</option>
))}
</select>
</div>
{/* Date */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Date
</label>
<input
type="date"
value={formData.date}
onChange={(e) => setFormData({...formData, date: e.target.value})}
className="w-full border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
required
/>
{/* Shop and Date Selection */}
<div className="flex space-x-4">
<div className="flex-1">
<label className="block text-sm font-medium text-gray-700 mb-2">
Shop
</label>
<select
value={formData.shop_id}
onChange={(e) => setFormData({...formData, shop_id: parseInt(e.target.value)})}
className="w-full h-10 border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
required
>
<option value={0}>Select a shop</option>
{shops.map(shop => (
<option key={shop.id} value={shop.id}>
{shop.name} - {shop.city}
</option>
))}
</select>
</div>
<div className="w-48">
<label className="block text-sm font-medium text-gray-700 mb-2">
Date
</label>
<input
type="date"
value={formData.date}
onChange={(e) => setFormData({...formData, date: e.target.value})}
className="w-full h-10 border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
required
/>
</div>
</div>
{/* Add Products Section */}
@@ -344,7 +348,7 @@ const AddShoppingEventModal: React.FC<AddShoppingEventModalProps> = ({
<select
value={newProductItem.product_id}
onChange={(e) => setNewProductItem({...newProductItem, product_id: parseInt(e.target.value)})}
className="w-full border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
className="w-full h-10 border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
>
<option value={0}>Select a product</option>
{Object.entries(
@@ -364,7 +368,7 @@ const AddShoppingEventModal: React.FC<AddShoppingEventModalProps> = ({
.sort((a, b) => a.name.localeCompare(b.name))
.map(product => (
<option key={product.id} value={product.id}>
{product.name}{product.organic ? '🌱' : ''}{product.brand ? ` (${product.brand.name})` : ''} {product.weight ? `${product.weight}${product.weight_unit}` : product.weight_unit}
{product.name}{product.organic ? ' 🌱' : ''}{product.weight ? ` ${product.weight}${product.weight_unit}` : product.weight_unit}{product.brand ? ` (${product.brand.name})` : ''}
</option>
))
}
@@ -391,7 +395,7 @@ const AddShoppingEventModal: React.FC<AddShoppingEventModalProps> = ({
placeholder="1"
value={newProductItem.amount}
onChange={(e) => setNewProductItem({...newProductItem, amount: parseFloat(e.target.value)})}
className="w-full border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
className="w-full h-10 border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
</div>
<div className="w-24">
@@ -405,14 +409,30 @@ const AddShoppingEventModal: React.FC<AddShoppingEventModalProps> = ({
placeholder="0.00"
value={newProductItem.price}
onChange={(e) => setNewProductItem({...newProductItem, price: parseFloat(e.target.value)})}
className="w-full border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
className="w-full h-10 border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
</div>
<div className="flex items-end">
<div className="w-20">
<label className="block text-xs font-medium text-gray-700 mb-1 text-center">
Discount
</label>
<div className="h-10 flex items-center justify-center border border-gray-300 rounded-md bg-gray-50">
<input
type="checkbox"
checked={newProductItem.discount}
onChange={(e) => setNewProductItem({...newProductItem, discount: e.target.checked})}
className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"
/>
</div>
</div>
<div className="w-16">
<label className="block text-xs font-medium text-gray-700 mb-1 opacity-0">
Action
</label>
<button
type="button"
onClick={addProductToEvent}
className="bg-green-500 hover:bg-green-700 text-white px-4 py-2 rounded-md"
className="w-full h-10 bg-green-500 hover:bg-green-700 text-white px-3 py-2 rounded-md font-medium"
>
Add
</button>
@@ -423,32 +443,52 @@ const AddShoppingEventModal: React.FC<AddShoppingEventModalProps> = ({
{selectedProducts.length > 0 && (
<div className="bg-gray-50 rounded-md p-4 max-h-40 overflow-y-auto">
<h4 className="font-medium text-gray-700 mb-2">Selected Items:</h4>
{selectedProducts.map((item, index) => (
<div key={index} className="flex justify-between items-center py-2 border-b last:border-b-0">
<div className="flex-1">
<div className="text-sm text-gray-900">
{getProductName(item.product_id)}
</div>
<div className="text-xs text-gray-600">
{item.amount} × ${item.price.toFixed(2)} = ${(item.amount * item.price).toFixed(2)}
</div>
</div>
<div className="flex space-x-2">
<button
type="button"
onClick={() => editProductFromEvent(index)}
className="text-blue-500 hover:text-blue-700"
>
Edit
</button>
<button
type="button"
onClick={() => removeProductFromEvent(index)}
className="text-red-500 hover:text-red-700"
>
Remove
</button>
{Object.entries(
selectedProducts.reduce((groups, item, index) => {
const product = products.find(p => p.id === item.product_id);
const category = product?.category.name || 'Unknown';
if (!groups[category]) {
groups[category] = [];
}
groups[category].push({ ...item, index });
return groups;
}, {} as Record<string, (ProductInEvent & { index: number })[]>)
)
.sort(([a], [b]) => a.localeCompare(b))
.map(([category, categoryItems]) => (
<div key={category} className="mb-3 last:mb-0">
<div className="text-xs font-semibold text-gray-600 uppercase tracking-wide mb-1 border-b border-gray-300 pb-1">
{category}
</div>
{categoryItems.map((item) => (
<div key={item.index} className="flex justify-between items-center py-2 pl-2">
<div className="flex-1">
<div className="text-sm text-gray-900">
{getProductName(item.product_id)}
<span className="text-xs text-gray-600 ml-2">
{item.amount} × ${item.price.toFixed(2)} = ${(item.amount * item.price).toFixed(2)}
{item.discount && <span className="ml-2 text-green-600 font-medium">🏷</span>}
</span>
</div>
</div>
<div className="flex space-x-2">
<button
type="button"
onClick={() => editProductFromEvent(item.index)}
className="text-blue-500 hover:text-blue-700"
>
Edit
</button>
<button
type="button"
onClick={() => removeProductFromEvent(item.index)}
className="text-red-500 hover:text-red-700"
>
Remove
</button>
</div>
</div>
))}
</div>
))}
</div>