From 33cb4e53283deeb040554f54d65dc8edb1ca0c7c Mon Sep 17 00:00:00 2001 From: Nils-Johan Gynther Date: Fri, 10 Apr 2026 18:57:21 +0200 Subject: [PATCH] Add InventoryList component for improved inventory display and search functionality --- frontend/app/inventory/InventoryList.tsx | 179 +++++++++++++++++++++++ frontend/app/inventory/page.tsx | 108 +------------- 2 files changed, 181 insertions(+), 106 deletions(-) create mode 100644 frontend/app/inventory/InventoryList.tsx diff --git a/frontend/app/inventory/InventoryList.tsx b/frontend/app/inventory/InventoryList.tsx new file mode 100644 index 00000000..1736e484 --- /dev/null +++ b/frontend/app/inventory/InventoryList.tsx @@ -0,0 +1,179 @@ +'use client'; + +import { useState } from 'react'; +import type { InventoryItem } from '../../features/inventory/types'; +import InventoryEditForm from './InventoryEditForm'; +import InventoryConsumeForm from './InventoryConsumeForm'; +import InventoryConsumptionHistory from './InventoryConsumptionHistory'; + +function formatDate(value: string | null) { + if (!value) return null; + return new Date(value).toLocaleDateString('sv-SE'); +} + +function getBestBeforeStatus(bestBeforeDate: string | null) { + if (!bestBeforeDate) { + return { label: 'Ingen bäst före angiven', color: '#666', background: '#f5f5f5', border: '#ddd' }; + } + const today = new Date(); + const bestBefore = new Date(bestBeforeDate); + today.setHours(0, 0, 0, 0); + bestBefore.setHours(0, 0, 0, 0); + const diffDays = Math.round((bestBefore.getTime() - today.getTime()) / (1000 * 60 * 60 * 24)); + if (diffDays < 0) return { label: 'Utgången', color: '#8b0000', background: '#ffeaea', border: '#f1b5b5' }; + if (diffDays <= 3) return { label: 'Snart utgången', color: '#8a4b00', background: '#fff4e5', border: '#f0cf9b' }; + return { label: 'OK', color: '#1f5f2c', background: '#ecf8ee', border: '#b9e0bf' }; +} + +type Props = { + inventory: InventoryItem[]; +}; + +export default function InventoryList({ inventory }: Props) { + const [search, setSearch] = useState(''); + + // Unika produktnamn för autocomplete + const autocompleteNames = Array.from( + new Set( + inventory.map((item) => item.product.canonicalName || item.product.name) + ) + ).sort(); + + // Filtrera baserat på söktext + const filtered = search.trim() + ? inventory.filter((item) => { + const q = search.trim().toLowerCase(); + const name = (item.product.canonicalName || item.product.name).toLowerCase(); + const brand = (item.brand || '').toLowerCase(); + const loc = (item.location || '').toLowerCase(); + const comment = (item.comment || '').toLowerCase(); + const suitable = (item.suitableFor || '').toLowerCase(); + return ( + name.includes(q) || + brand.includes(q) || + loc.includes(q) || + comment.includes(q) || + suitable.includes(q) + ); + }) + : inventory; + + return ( +
+

Aktuella hemmavaror (inventory)

+ + {/* Sökfält */} +
+ setSearch(e.target.value)} + placeholder="Sök vara, varumärke, plats..." + style={{ + width: '100%', + padding: '0.6rem 0.75rem', + border: '1px solid #ccc', + borderRadius: '6px', + fontSize: '1rem', + }} + /> + + {autocompleteNames.map((name) => ( + + {search && ( +
+ {filtered.length} av {inventory.length} varor visas +
+ )} +
+ + {filtered.length === 0 ? ( +

Inga hemmavaror matchar sökningen.

+ ) : ( +
+ {filtered.map((item) => { + const bestBeforeStatus = getBestBeforeStatus(item.bestBeforeDate); + return ( +
+
+
+ + {item.product.canonicalName || item.product.name} + +
+ {item.quantity} {item.unit} +
+
+ +
+ {bestBeforeStatus.label} +
+
+ +
+ {item.location ?
Plats: {item.location}
: null} + {item.brand ?
Varumärke: {item.brand}
: null} +
Öppnad: {item.opened ? 'Ja' : 'Nej'}
+ {item.suitableFor ?
Passar till: {item.suitableFor}
: null} + {item.bestBeforeDate ?
Bäst före: {formatDate(item.bestBeforeDate)}
: null} + {item.comment ?
Kommentar: {item.comment}
: null} +
+ +
+ + + +
+
+ ); + })} +
+ )} +
+ ); +} diff --git a/frontend/app/inventory/page.tsx b/frontend/app/inventory/page.tsx index a6f508f7..1e0bd42f 100644 --- a/frontend/app/inventory/page.tsx +++ b/frontend/app/inventory/page.tsx @@ -1,11 +1,9 @@ import InventoryForm from './InventoryForm'; -import InventoryEditForm from './InventoryEditForm'; -import InventoryConsumeForm from './InventoryConsumeForm'; import ProductForm from './ProductForm'; import Link from 'next/link'; import { fetchJson } from '../../lib/api'; import type { InventoryItem, Product } from '../../features/inventory/types'; -import InventoryConsumptionHistory from './InventoryConsumptionHistory'; +import InventoryList from './InventoryList'; function formatDate(value: string | null) { if (!value) return null; @@ -179,109 +177,7 @@ export default async function InventoryPage({ searchParams }: InventoryPageProps -
-

Aktuella hemmavaror (inventory)

- - {inventory.length === 0 ? ( -

Inga hemmavaror för det valda filtret.

- ) : ( -
- {inventory.map((item) => { - const bestBeforeStatus = getBestBeforeStatus(item.bestBeforeDate); - - return ( -
-
-
- - {item.product.canonicalName || item.product.name} - -
- {item.quantity} {item.unit} -
-
- -
- {bestBeforeStatus.label} -
-
- -
- {item.location ?
Plats: {item.location}
: null} - - {item.brand ?
Varumärke: {item.brand}
: null} - -
Öppnad: {item.opened ? 'Ja' : 'Nej'}
- - {item.suitableFor ? ( -
Passar till: {item.suitableFor}
- ) : null} - - {item.bestBeforeDate ? ( -
Bäst före: {formatDate(item.bestBeforeDate)}
- ) : null} - - {item.comment ?
Kommentar: {item.comment}
: null}
- -
- - - -
-
- ); - })} -
- )} -
+ ); } \ No newline at end of file