diff --git a/NEXT_STEPS.md b/NEXT_STEPS.md index bfb2a63b..77030d69 100644 --- a/NEXT_STEPS.md +++ b/NEXT_STEPS.md @@ -16,7 +16,7 @@ | Matplanering (veckovy, inköpslista) | ✅ Klart | | Matplan — portionsjustering per dag | ✅ Klart | | Matplan — inventariejämförelse (backend) | ✅ Klart | -| Matplan — inventariejämförelse (frontend-vy) | ⚠️ Grundläggande, saknar ✅/⚠️/❌-status | +| Matplan — inventariejämförelse (frontend-vy) | ✅ Klart (✅/⚠️/❌ integrerat i inköpslistan) | | Baslager (lista, lägg till, ta bort) | ✅ Klart | | Admin: Produkter (edit, merge, duplicate, restore, reset) | ✅ Klart | | Admin: Bulk-kategorisering | ✅ Klart | @@ -61,13 +61,15 @@ Idag har alla inloggade användare samma behörighetsnivå — ett säkerhetspro - **Frontend — admin-UI (`/admin/users/`):** Lista användare, skapa nya konton (namn, e-post, lösenord, roll), ändra roll, avaktivera konto - **Frontend — skyddade routes:** `/admin/*` kräver admin-roll; omdirigerar annars till startsidan -### 3. Matplan-vy (frontend-polish) -**Mål:** Ge användare tydlig feedback på lagerstatus och underlätta inköp. +### 3. Matplan-vy (frontend-polish) ✅ +**Klart.** -Backend-endpointen `GET /api/meal-plan/inventory-compare?from=...&to=...` finns och fungerar. Det som saknas är en tydlig frontend-vy: -- Visa inköpslistan med statusindikatorer: ✅ Finns hemma / ⚠️ Delvis / ❌ Saknas -- Aggregera `inventory-compare`-svaret per ingrediens över hela veckan -- Möjlig placering: ny flik i matplanen eller sidopanel i veckovy +Inköpslistan och inventariejämförelsen är sammanslagna till en enhetlig vy med tre statusnivåer: +- ❌ Saknas helt — visar hur mycket som behövs köpas +- ⚠️ Delvis hemma — visar hur mycket mer som behövs + vad som finns +- ✅ Finns hemma — markeras nedtonat, ingen köpindikering + +Listan sorteras automatiskt: saknade ingredienser överst, hemma-ingredienser underst. En sammanfattningsrad visar totalt antal per statuskategori. ### 4. Teknisk skuld (underhåll) **Mål:** Minska komplexitet och risk för buggar. diff --git a/frontend/app/matplan/MealPlanClient.tsx b/frontend/app/matplan/MealPlanClient.tsx index 82d7d7ce..ddbf407c 100644 --- a/frontend/app/matplan/MealPlanClient.tsx +++ b/frontend/app/matplan/MealPlanClient.tsx @@ -217,7 +217,7 @@ export default function MealPlanClient({ recipes }: { recipes: Recipe[] }) { })} - {/* Samlad ingredienslista */} + {/* Inköpslista med lagerstatus */}

Inköpslista ({plannedCount} {plannedCount === 1 ? 'recept' : 'recept'} planerade) @@ -226,80 +226,101 @@ export default function MealPlanClient({ recipes }: { recipes: Recipe[] }) {

Välj recept ovan för att se en samlad ingredienslista.

) : shopping.length === 0 ? (

Laddar ingredienser...

- ) : ( - - )} -

+ ) : (() => { + // Berika varje rad med inventariestatus + type DisplayStatus = 'enough' | 'partial' | 'missing'; + const enriched = shopping.map((item) => { + const cmp = inventoryCompare.find( + (c) => c.productId === item.productId && c.unit === item.unit, + ); + let displayStatus: DisplayStatus = 'missing'; + let buyQty = item.quantity; + if (cmp) { + if (cmp.available >= cmp.required) { + displayStatus = 'enough'; + buyQty = 0; + } else if (cmp.available > 0) { + displayStatus = 'partial'; + buyQty = cmp.missing; + } + } + return { ...item, cmp, displayStatus, buyQty }; + }); - {/* Inventariejämförelse */} - {plannedCount > 0 && inventoryCompare.length > 0 && ( -
-

Inventariegranskning

-

- Vad du har hemma vs. vad veckans recept kräver. -

- {(() => { - const missingCount = inventoryCompare.filter((i) => i.status === 'missing').length; - return missingCount === 0 ? ( -

- ✓ Du har allt hemma! -

- ) : ( -

- {missingCount} ingrediens{missingCount !== 1 ? 'er' : ''} saknas eller räcker inte -

+ const order: Record = { missing: 0, partial: 1, enough: 2 }; + enriched.sort((a, b) => order[a.displayStatus] - order[b.displayStatus] || a.name.localeCompare(b.name, 'sv')); + + const missingCount = enriched.filter((e) => e.displayStatus === 'missing').length; + const partialCount = enriched.filter((e) => e.displayStatus === 'partial').length; + const enoughCount = enriched.filter((e) => e.displayStatus === 'enough').length; + const hasCompare = inventoryCompare.length > 0; + + const fmtQty = (n: number) => (n % 1 === 0 ? String(n) : n.toFixed(1)); + + return ( + <> + {/* Sammanfattning */} + {hasCompare && ( +
+ {missingCount > 0 && ❌ {missingCount} saknas} + {partialCount > 0 && ⚠️ {partialCount} delvis hemma} + {enoughCount > 0 && ✅ {enoughCount} hemma} + {missingCount === 0 && partialCount === 0 && ( + ✅ Du har allt hemma! + )} +
+ )} + + + ); - })()} - -
- )} + })() + } + )}