From 03361f7b7dfef74ada77083251cccd6e06f9f989 Mon Sep 17 00:00:00 2001 From: Nils-Johan Gynther Date: Thu, 9 Apr 2026 23:25:52 +0200 Subject: [PATCH] Refactor inventory forms to include unit and location options; update quantity input handling --- frontend/app/admin/products/page.tsx | 20 +----- frontend/app/api/products/route.ts | 19 +++++ .../app/inventory/InventoryConsumeForm.tsx | 21 ++++-- frontend/app/inventory/InventoryEditForm.tsx | 66 ++++++++++++++--- frontend/app/inventory/InventoryForm.tsx | 71 +++++++++++++++---- frontend/app/inventory/page.tsx | 19 +---- frontend/app/page.tsx | 19 +---- .../app/recipes/create/CreateRecipePage.tsx | 37 ++++++++-- frontend/app/recipes/page.tsx | 11 +++ 9 files changed, 191 insertions(+), 92 deletions(-) create mode 100644 frontend/app/api/products/route.ts diff --git a/frontend/app/admin/products/page.tsx b/frontend/app/admin/products/page.tsx index 77d1a7d1..6caaa628 100644 --- a/frontend/app/admin/products/page.tsx +++ b/frontend/app/admin/products/page.tsx @@ -2,31 +2,13 @@ import { fetchJson } from '../../../lib/api'; import type { Product } from '../../../features/inventory/types'; import CanonicalNameForm from './CanonicalNameForm'; import MergePreviewForm from './MergePreviewForm'; -import Link from 'next/link'; export default async function AdminProductsPage() { const products = await fetchJson('/api/products'); return (
-
-

Admin: Produkter

- - Lägg till nytt recept - -
+

Admin: Produkter

Här kan du granska och standardisera produktnamn.

diff --git a/frontend/app/api/products/route.ts b/frontend/app/api/products/route.ts new file mode 100644 index 00000000..76b0a96e --- /dev/null +++ b/frontend/app/api/products/route.ts @@ -0,0 +1,19 @@ +import { NextRequest, NextResponse } from 'next/server'; + +const API_BASE = process.env.NEXT_PUBLIC_API_URL_INTERNAL || 'http://recipe-api:8080'; + +export async function GET(request: NextRequest) { + const res = await fetch(`${API_BASE}/api/products`, { + method: 'GET', + cache: 'no-store', + }); + + const text = await res.text(); + + return new NextResponse(text, { + status: res.status, + headers: { + 'Content-Type': 'application/json', + }, + }); +} diff --git a/frontend/app/inventory/InventoryConsumeForm.tsx b/frontend/app/inventory/InventoryConsumeForm.tsx index 9790587d..b052ac0a 100644 --- a/frontend/app/inventory/InventoryConsumeForm.tsx +++ b/frontend/app/inventory/InventoryConsumeForm.tsx @@ -38,10 +38,12 @@ export default function InventoryConsumeForm({ id, unit }: Props) { onSubmit={(e) => { e.preventDefault(); setError(null); - const form = e.currentTarget; const formData = new FormData(form); - + const raw = formData.get('amountUsed') as string; + const { quantity, unit: parsedUnit } = parseQuantityInput(raw, unit); + formData.set('amountUsed', String(quantity)); + formData.set('unit', parsedUnit); startTransition(async () => { try { await consumeInventoryItem(formData); @@ -67,9 +69,7 @@ export default function InventoryConsumeForm({ id, unit }: Props) {
@@ -109,4 +109,15 @@ export default function InventoryConsumeForm({ id, unit }: Props) { {error ?

{error}

: null} ); +} + +function parseQuantityInput(input: string, defaultUnit: string) { + const match = input.trim().match(/^([\d.,]+)\s*([a-zA-Z]*)$/); + if (!match) return { quantity: NaN, unit: defaultUnit }; + let [, num, unit] = match; + num = num.replace(',', '.'); + unit = unit || defaultUnit; + if (defaultUnit === 'kg' && (unit === 'g' || unit === 'gram')) return { quantity: parseFloat(num) / 1000, unit: 'kg' }; + if (defaultUnit === 'g' && (unit === 'kg' || unit === 'kilogram')) return { quantity: parseFloat(num) * 1000, unit: 'g' }; + return { quantity: parseFloat(num), unit }; } \ No newline at end of file diff --git a/frontend/app/inventory/InventoryEditForm.tsx b/frontend/app/inventory/InventoryEditForm.tsx index b65fa516..2c3957e1 100644 --- a/frontend/app/inventory/InventoryEditForm.tsx +++ b/frontend/app/inventory/InventoryEditForm.tsx @@ -13,6 +13,38 @@ function toDateInputValue(value: string | null) { return value.slice(0, 10); } +function parseQuantityInput(input: string, defaultUnit: string) { + const match = input.trim().match(/^([\d.,]+)\s*([a-zA-Z]*)$/); + if (!match) return { quantity: NaN, unit: defaultUnit }; + let [, num, unit] = match; + num = num.replace(',', '.'); + unit = unit || defaultUnit; + if (defaultUnit === 'kg' && (unit === 'g' || unit === 'gram')) return { quantity: parseFloat(num) / 1000, unit: 'kg' }; + if (defaultUnit === 'g' && (unit === 'kg' || unit === 'kilogram')) return { quantity: parseFloat(num) * 1000, unit: 'g' }; + return { quantity: parseFloat(num), unit }; +} + +const UNIT_OPTIONS = [ + { value: '', label: 'Välj enhet' }, + { value: 'g', label: 'g (gram)' }, + { value: 'kg', label: 'kg (kilogram)' }, + { value: 'hg', label: 'hg (hektogram)' }, + { value: 'ml', label: 'ml (milliliter)' }, + { value: 'dl', label: 'dl (deciliter)' }, + { value: 'l', label: 'l (liter)' }, + { value: 'st', label: 'st (styck)' }, + { value: 'tsk', label: 'tsk (tesked)' }, + { value: 'msk', label: 'msk (matsked)' }, +]; + +const LOCATION_OPTIONS = [ + { value: '', label: 'Välj plats' }, + { value: 'Kyl', label: 'Kyl' }, + { value: 'Frys', label: 'Frys' }, + { value: 'Skafferi', label: 'Skafferi' }, + { value: 'Annat', label: 'Annat' }, +]; + export default function InventoryEditForm({ item }: Props) { const [isEditing, setIsEditing] = useState(false); const [isPending, startTransition] = useTransition(); @@ -43,10 +75,13 @@ export default function InventoryEditForm({ item }: Props) { onSubmit={(e) => { e.preventDefault(); setError(null); - const form = e.currentTarget; const formData = new FormData(form); - + const raw = formData.get('quantity') as string; + const unit = formData.get('unit') as string; + const { quantity, unit: parsedUnit } = parseQuantityInput(raw, unit); + formData.set('quantity', String(quantity)); + formData.set('unit', parsedUnit); startTransition(async () => { try { await updateInventoryItem(formData); @@ -79,9 +114,8 @@ export default function InventoryEditForm({ item }: Props) {
@@ -90,23 +124,33 @@ export default function InventoryEditForm({ item }: Props) {