Files
recipe-app/frontend/app/baslager/PantryList.tsx
T

149 lines
5.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client';
import { useRouter } from 'next/navigation';
type PantryItem = {
id: number;
product: { id: number; name: string; canonicalName: string | null; category: string | null };
};
type InventoryItem = {
productId: number;
quantity: string;
unit: string;
};
type Props = {
items: PantryItem[];
inventoryByProductId: Record<number, InventoryItem[]>;
onDeleted?: () => void;
};
export default function PantryList({ items, inventoryByProductId, onDeleted }: Props) {
const router = useRouter();
async function handleRemove(id: number, name: string) {
if (!confirm(`Ta bort "${name}" från baslagret?`)) return;
const res = await fetch(`/api/admin/pantry-item/${id}`, { method: 'DELETE' });
if (res.ok) {
if (onDeleted) onDeleted();
else router.refresh();
}
}
if (items.length === 0) {
return (
<p style={{ color: '#888', fontStyle: 'italic' }}>
Baslagret är tomt. Lägg till produkter ovan.
</p>
);
}
// Gruppera per kategori
const grouped = items.reduce<Record<string, PantryItem[]>>((acc, item) => {
const cat = item.product.category || 'Övrigt';
if (!acc[cat]) acc[cat] = [];
acc[cat].push(item);
return acc;
}, {});
const sortedCategories = Object.keys(grouped).sort((a, b) => {
if (a === 'Övrigt') return 1;
if (b === 'Övrigt') return -1;
return a.localeCompare(b, 'sv');
});
return (
<div style={{ display: 'grid', gap: '1.5rem' }}>
{sortedCategories.map((category) => (
<section key={category}>
<h3 style={{ margin: '0 0 0.5rem', fontSize: '1rem', color: '#555', fontWeight: 600 }}>
{category}
</h3>
<div style={{ display: 'grid', gap: '0.4rem' }}>
{grouped[category].map((item) => {
const displayName = item.product.canonicalName || item.product.name;
const invItems = inventoryByProductId[item.product.id] || [];
const hasInventory = invItems.length > 0;
// Summera per enhet för visning
const unitSummary = invItems.reduce<Record<string, number>>((acc, inv) => {
const u = inv.unit || '?';
acc[u] = (acc[u] || 0) + parseFloat(inv.quantity || '0');
return acc;
}, {});
const inventoryText = Object.entries(unitSummary)
.map(([u, q]) => `${q % 1 === 0 ? q : q.toFixed(1)} ${u}`)
.join(', ');
return (
<div
key={item.id}
style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
padding: '0.6rem 0.75rem',
border: '1px solid #eee',
borderRadius: '6px',
background: '#fafafa',
}}
>
<div style={{ display: 'flex', alignItems: 'center', gap: '0.6rem', minWidth: 0 }}>
<span style={{ minWidth: '1.1rem', textAlign: 'center', fontSize: '1rem' }}>
{hasInventory ? '✓' : '○'}
</span>
<span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{displayName}</span>
{hasInventory ? (
<span style={{
fontSize: '0.78rem',
color: '#1f5f2c',
background: '#ecf8ee',
border: '1px solid #b9e0bf',
borderRadius: '4px',
padding: '0.1rem 0.45rem',
whiteSpace: 'nowrap',
}}>
{inventoryText}
</span>
) : (
<span style={{
fontSize: '0.78rem',
color: '#888',
background: '#f5f5f5',
border: '1px solid #ddd',
borderRadius: '4px',
padding: '0.1rem 0.45rem',
whiteSpace: 'nowrap',
}}>
Saknas i inventariet
</span>
)}
</div>
<button
type="button"
onClick={() => handleRemove(item.id, displayName)}
style={{
background: 'none',
border: 'none',
color: '#c00',
cursor: 'pointer',
fontSize: '1.1rem',
padding: '0.2rem 0.5rem',
lineHeight: 1,
flexShrink: 0,
}}
title="Ta bort från baslagret"
>
×
</button>
</div>
);
})}
</div>
</section>
))}
</div>
);
}