Files
recipe-app/NEXT_STEPS.md
T

328 lines
18 KiB
Markdown
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.
# Nästa steg
> Förslag på vad vi kan ta tag i nästa gång vi öppnar projektet.
> Se [README.md](README.md) för funktionsöversikt och [TEKNISK_BESKRIVNING.md](TEKNISK_BESKRIVNING.md) för teknisk detaljerinformation.
> Se [AI-FUNKTIONER.md](AI-FUNKTIONER.md) för planerade AI-funktioner.
---
|---|---|
| Inventorie (CRUD, konsumtion, historik) | ✅ Klart |
| Recept (skapa, visa, importera, matchning) | ✅ Klart |
| Receptredigering och borttagning (JWT-autentisering) | ✅ Klart |
| Snabbimport (URL/PDF/bild/ICA) | ✅ Klart |
| Kvittoimport (Mistral AI, OCR, alias) | ✅ Klart |
| Matplanering (veckovy, inköpslista) | ✅ Klart |
| Matplan — portionsjustering per dag | ✅ Klart |
| Matplan — inventariejämförelse (backend) | ✅ Klart |
| Matplan — inventariejämförelse (frontend-vy) | ✅ Klart (✅/⚠️/❌ integrerat i inköpslistan) |
| Baslager (lista, lägg till, ta bort) | ✅ Klart (global v1, user-scope planerad) |
| Admin: Produkter (edit, merge, duplicate, restore, reset) | ✅ Klart |
| Admin: Bulk-kategorisering | ✅ Klart |
| Receptredigering (frontend UX) | ✅ Klart |
| Receptbilder (upload URL) | ✅ Klart |
| Autentisering (JWT, Auth.js v5, User-modell) | ✅ Klart |
| Klient-side JWT via useAuthFetch() | ✅ Klart |
| SessionProvider i root layout | ✅ Klart |
| Användarprofil (firstName, lastName, email) | ✅ Klart |
| Produktkategorier — hierarkisk struktur (3 nivåer) | ✅ Klart |
| Kategori-seed (seed_all.sql, komplett träd, enda sanningskällan) | ✅ Klart |
| Kategoritilldelning i admin-UI | ✅ Klart |
| Taggning av produkter | ✅ Klart |
| Näringsvärden på produkter | ✅ Klart (schema + API) |
| Seed produktdata med kategoritilldelning | ✅ Klart (seed_all.sql) |
| Användarspecifika produkter (UserProduct) | ⚠️ Schema klart, UI basic |
| Användarroller (user / admin) | ✅ Klart |
| Användarhantering i admin-UI | ✅ Klart |
| Profilsida med flikar (Min profil / Användare / Databas med undertabbar) | ✅ Klart |
| Teknisk skuld — oanvända InventoryItem-fält | ✅ Klart (migration 20260418) |
| Teknisk skuld — redirect-routes städade | ✅ Klart |
| Premium-plan (isPremium på User, Free/Paid-dropdown) | ✅ Klart |
| AI-modul (AiService, Mistral-kategorisering, fallback) | ✅ Klart |
| Admin: AI-kategorisering per produkt ("Fråga AI") | ✅ Klart |
| Admin: AI-bulk-kategorisering av okategoriserade produkter | ✅ Klart |
| Produktstatus (pending / active / rejected) | ✅ Klart |
| Admin: Väntande produktförslag (pending-sida) | ✅ Klart |
| Kvittoimport — AI-kategorisuggestion för premium-användare | ✅ Klart |
| Säkerhetshuvuden — Caddy (globala headers) | ✅ Klart |
| Säkerhetshuvuden — Next.js CSP | ✅ Klart |
| Säkerhetshuvuden — NestJS Helmet (backup) | ✅ Klart (aktiveras vid nästa rebuild) |
| Buggfix: "Vad behöver jag köpa" — flimmer och scroll | ✅ Klart |
| Startsida — produktadmin-länk borttagen | ✅ Klart |
| Avancerad AI-integration (veckoplanering, receptförslag) | ❌ Planerad |
| EAN-skanning via Open Food Facts API | ❌ Planerad |
## Status — senast genomgånget: 2026-04-22
### Nyheter och förbättringar
- **User-scope för pantry och matplan** — Alla baslager- och matplansdata är nu per användare. Backend och Prisma-schema är migrerade.
- **Robust bildimport** — Bild-URL normaliseras, laddas ner och optimeras i backend. Bilden kopplas till receptet och raderas vid delete. Diagnostikloggning på alla steg.
- **Importflöde** — Quick-import och receipt-import har förbättrats med robust multipart-hantering, timeout, och felhantering. Markdown och bild-url skickas hela vägen till UI.
- **Flutter-parity** — Matplan, inventarie, baslager och receptflöden är nu fullt migrerade till Flutter med user-scope och robust felhantering.
- **Felsökningslogg** — Se `IMPORT_IMAGE_DEBUG_2026-04-22.md` för detaljerad felsökningshistorik kring bildimport och importflöde.
### Kända begränsningar
- Kvittoimport (Fas 6b) är påbörjad men granskningssteg och bulk-spara återstår.
- Bildimport kräver att containrar är uppdaterade med senaste kod — kontrollera att diagnostikloggar syns vid felsökning.
- Vissa adminfunktioner och avancerad AI-integration är planerade men ej migrerade.
---
## Nästa steg
1. Slutför kvittoimport (granskningssteg och bulk-spara i Flutter).
2. Fortsatt flytt av UI-strängar till ARB (inventarie, pantry, recept).
3. Smoke-test på testdomän och avstämning.
4. Planera och påbörja avancerad AI-integration och EAN-skanning.
---
## Beslut 2026-04-22 — Pantry och matplan på user-nivå
Pantry och matplan ska inte vara globala. De ska vara användarspecifika i backend.
### Varför
- Nuvarande implementation delar baslager och matplan mellan alla användare.
- Det ger felaktig inköpslogik och otydligt ägarskap i multi-user-läge.
### Krävda backend-ändringar (prio)
1. **PantryItem user-scope i Prisma**
- Lägg till `userId` i `PantryItem` + relation till `User`.
- Ändra unik regel från global `productId` till kombinationen `userId + productId`.
- Lägg index på `userId`.
2. **Pantry API user-scope**
- `GET /api/pantry` ska returnera inloggad användares baslager.
- `POST /api/pantry` ska skapa för inloggad användare.
- `DELETE /api/pantry/:id` ska bara kunna ta bort poster som ägs av inloggad användare.
3. **MealPlan user-scope i Prisma och service**
- Lägg till `userId` i `MealPlanEntry` + relation till `User`.
- Ändra unik regel från global `date` till `userId + date`.
- Filtrera `findByRange`, `upsert` och `removeByDate` per inloggad användare.
4. **Matplanens inventory-compare ska använda user-scope pantry**
- Pantry-uppslag i `inventoryCompare` ska filtreras på samma `userId`.
### Migrering och release-strategi
1. Ta migration för `PantryItem` + `MealPlanEntry`.
2. Backfill: tilldela befintliga poster till vald default-användare (eller rensa och starta om datan i testmiljö).
3. Uppdatera backend-kontrollers till `@CurrentUser()`-mönster.
4. Verifiera med två testanvändare att data är isolerad.
5. Därefter uppdateras Flutter/Next-klienterna mot de nya kontrakten.
---
## Notering 2026-04-20
## Notering 2026-04-21
- Rollskydd i frontend (middleware) och backend (admin på endpoints) är nu heltäckande.
- Rate limiting, CORS-låsning och JWT_SECRET-krav är på plats.
- Konsol-loggar borttagna ur backend.
- Kvar: Kryptering av PII (e-post, namn i databasen), automatisering av seed_all.sql vid fresh install.
Den senaste versionen av `db/seeds/seed_all.sql` är pushad till utvecklingsmiljön, men har **ännu inte körts** på databasen. Nästa gång vi vill uppdatera kategoriträdet måste seed-scriptet köras manuellt:
```bash
docker exec -i recipe-db mariadb -uroot -p"$DB_PASS" recipe_app < db/seeds/seed_all.sql
```
Se även TEKNISK_BESKRIVNING.md för detaljer om seed-processen.
---
## Vägen till produktlansering
- [Säkerhet & Data](produktlansering.md#1-säkerhet--data)
- [DevOps & Stabilitet](produktlansering.md#2-devops--stabilitet)
- [Testning & Kvalitet](produktlansering.md#3-testning--kvalitet)
- [Funktionella förbättringar](produktlansering.md#4-funktionella-förbättringar)
- [Risker & Flaskhalsar](produktlansering.md#5-risker--flaskhalsar)
---
## Prioriterade förbättringar
### 1. ICA-skraparen missar receptbild
**Problem:** Vid import av ICA-recept via URL hämtas inte receptbilden korrekt.
- Felsök parsern i `recipe-document-converter/src/parser.ts` — kontrollera att ICA:s bild-selektor fortfarande matchar sidans HTML-struktur
- ICA kan ha ändrat klassnamn eller lazy-load-mekanism sedan skraparen skrevs
- Verifiera med ett konkret ICA-recept-URL och logga vad `imageUrl` returnerar
### 2. Seed-data ✅
**Klart.** `db/seeds/seed_all.sql` är den enda sanningskällan för kategorier och produkter.
Filen:
- TRUNCATEar `Category` och nollställer `Product.categoryId`
- Bygger hela kategoriträdet (nivå 13) utan dubbletter
- Insertar ~190 produkter (INSERT IGNORE)
- Kopplar produkterna till rätt kategori via JOIN-subqueries
Körs manuellt: `docker exec -i recipe-db mariadb -uroot -p"$DB_PASS" recipe_app < db/seeds/seed_all.sql`
### 2. Refactor: seed_all.sql som del av fresh install
**Mål:** Fresh install ska inte kräva ett manuellt seed-steg.
Nuläge:
- `db/init/001-init.sql` (MariaDB auto-init) skapar bara en bootstrap-check-tabell
- Prisma-migrationen `20260417310000_add_category_tree` seedar kategorier en gång, men seed_all.sql skriver över dem
- `seed_all.sql` körs **inte** automatiskt vid deploy eller fresh install
Alternativ:
- **Alt A:** Flytta seed_all.sql-logiken till `db/init/001-init.sql` — körs automatiskt av MariaDB-containern vid första start
- **Alt B:** Skapa en `prisma/seed.ts` som körs via `prisma db seed` — standard NestJS/Prisma-mönster, kan inkluderas i deploy.sh
- **Alt C:** Lägg till `seed_all.sql` som ett steg i `deploy.sh` (körs alltid, idempotent p.g.a. TRUNCATE+INSERT IGNORE)
Oavsett alternativ bör de gamla kategori-INSERTs i `20260417310000_add_category_tree/migration.sql` tas bort eller kommenteras ur för att undvika förvirring.
### 2. Användarroller och full användarhantering ✅
**Klart.**
Systemet har nu fullständig rollbaserad åtkomstkontroll och ett komplett användarhanteringsgränssnitt inbyggt i profilsidan.
**Rollsystemet:**
- **Prisma-migration** (`20260418100000_add_user_role`) — fältet `role String @default("user")` lades till på `User`-modellen
- **`@Roles('admin')`-dekoratorn** (`auth/decorators/roles.decorator.ts`) — använder `SetMetadata` för att markera endpoints
- **`RolesGuard`** (`auth/roles.guard.ts`) — registrerad globalt som `APP_GUARD`; läser rollmetadata, kastar 403 om rätt roll saknas
- **JWT inkluderar nu `role`** — `jwt.strategy.ts` returnerar `{userId, username, role}`, `auth.service.ts` signerar med `role` i payload
- **Bootstrap-användare** (`users/admin-bootstrap.service.ts`) — `OnApplicationBootstrap` skapar/uppdaterar Nadmin, Padmin, user1 och user2 vid varje uppstart via miljövariabler
- **Skyddade produkt-endpoints** — `@Roles('admin')``merge`, `delete`, `restore`, `reset-all`, `bulk-update`, `backfill-canonical` i `products.controller.ts`
**Backend-endpoints för användarhantering (alla kräver admin-roll):**
- `GET /api/users` — lista alla användare
- `PATCH /api/users/:id/role` — ändra roll
- `POST /api/users` — skapa ny användare (validering: unikt användarnamn och e-post)
- `DELETE /api/users/:id` — ta bort användare (skyddad: kan inte ta bort sig själv)
- `POST /api/users/:id/reset-password` — genererar tillfälligt lösenord, returnerar meddelandetext + lösenord
- `PATCH /api/users/:id/email` — uppdatera e-postadress
**Profilsidan med flikar (`/profil`):**
- `?tab=profil` — Min profil (alla användare)
- `?tab=anvandare` — Användare (enbart admin): skapa, ta bort, rollbyte, e-postbyte, lösenordsåterställning med kopierings-modal
- `?tab=databas` — Databas (enbart admin): produktadmin, nu med undertabbar:
- **Varor:** lista och redigera aktiva produkter
- **Skapa / Slå ihop:** skapa ny produkt, återställ produktdatabas, slå ihop dubbletter
- **Papperskorg:** visa mjukraderade produkter, återställ eller radera permanent
- `/admin/users` omdirigerar till `/profil?tab=anvandare`
- Navigeringslänken "👥 Användare" går direkt till `/profil?tab=anvandare`
### 3. Matplan-vy (frontend-polish) ✅
**Klart.**
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.
#### A. CanonicalNameForm och NameForm ✅
Filerna var redan borttagna — inga aktiva imports hittades. Inget att göra.
#### B. Oanvända fält på InventoryItem ✅
Följande 6 fält togs bort via Prisma-migration (`20260418000000_remove_unused_inventory_fields`):
`priority`, `shelfNote`, `isOnSale`, `priceLevel`, `proteinType`, `isLeftover`
- Schema, DTOs (create + update), service och frontend-typen är städade.
- `opened` och `suitableFor` behölls — de används i UI.
#### C. Validering av DTO:er i admin-actions ✅
Redan implementerat — `trim()` + max 100 tecken på alla fält i `actions.ts`. Inget att göra.
#### D. Routing-städning för kvitto och import ✅
`frontend/app/kvitto/page.tsx` och `frontend/app/recipes/import/page.tsx` är borttagna.
`/import` täcker båda use-cases via flikar.
#### E. Seed-data i versionshantering ✅
`data/matvaror_sverige.csv` och `data/seed_products.sql` behålls i git för reproducerbarhet. Inga ändringar i `.gitignore`.
### 5. Avancerad AI-integration
**Mål:** Smarta receptförslag och veckoplanering baserat på inventarie och kampanjdata.
AI-infrastrukturen är nu på plats (`AiService`, `mistral-small-2603`, premium-plan). Kategorisering för produkter och kvittoimport är implementerat. Nästa steg:
- **"Vad ska jag laga idag?"** — Receptförslag baserat på vad som finns i inventariet; kan byggas direkt ovanpå befintliga inventory- och recipe-endpoints
- **Veckoplanering med AI** — Generera ett veckoschemat baserat på inventarie, preferenser och ev. kampanjpriser (kräver extern datakälla)
- Kräver: tydlig API-design, kostnadskontroll och modellval per use-case
---
## AI-loggning och audit
- detta behöver ske innan nästa Ai-integration (receptförslag, veckoplanering) implementeras, för att säkerställa att vi har en robust lösning på plats för att spåra och analysera AI-anrop och resultat
- Utforska och utveckla lösning för loggning och audit av AI-input och output
- Syfte: möjliggöra spårbarhet, felsökning och kvalitetssäkring av AI-funktioner
- Implementera loggning på ett sätt som inte lagrar personuppgifter eller känslig data i klartext
- Utvärdera om loggar ska lagras i separat databas eller fil, och hur de kan användas för analys och förbättring
---
## Enhetsstöd och konvertering
- Se över om vi behöver utöka antalet stödda enhetstyper (t.ex. fler volym- eller portionsenheter)
- Utveckla och förbättra normaliseringen i `recipeservice.normalizeUnit`
- Se över och testa konverteringslogiken för enheter (t.ex. edge cases, felhantering)
- Fundera på om AI kan användas för att tolka, normalisera eller konvertera enheter och mängder smartare
---
## Enhetstester
Jest + ts-jest är uppsatt. Tester finns för:
- `normalize-name.ts` — 10 tester
- `base.parser.ts` (`parseIngredientLine`) — 12 tester
- `recipes.service.ts` (`normalizeUnit`, `convertUnit`) — 17 tester
Kör med `npm test` i `backend/`.
---
## Planerade funktioner
### Säkerhet — kryptering av känslig användardata
**Mål:** Kryptera känsliga fält (t.ex. e-postadresser, personuppgifter) i databasen med en hybridlösning.
Förslag på approach:
- **AES-256-GCM** för symmetrisk kryptering av data i vila (kolumnnivå i MariaDB eller på applikationsnivå i Prisma middleware)
- **Nyckelhantering** via miljövariabel eller extern nyckelhanteringstjänst (t.ex. HashiCorp Vault)
- Kräver genomtänkt migrering av befintlig data och påverkar sökbarhet (krypterade fält kan inte indexeras direkt)
### EAN-skanning — import via streckkod (Open Food Facts)
**Mål:** Låt användaren skanna eller skriva in en EAN-streckkod och få produktinformation (namn, kategori, näringsvärden) ifyllt automatiskt.
Open Food Facts är ett öppet, gratis API utan API-nyckel för grundläggande användning.
**Exempelanrop (EAN 7310960010016):**
```
GET https://world.openfoodfacts.org/api/v0/product/{ean}.json
```
Svaret innehåller:
- `product.product_name` — produktnamn
- `product.categories` — kategoristräng (kommaseparerad)
- `product.nutriments` — näringsvärden (energi, protein, fett, kolhydrater, m.fl.)
- `product.code` — EAN-koden bekräftad
**Förslag på implementation:**
1. **Backend-endpoint**`GET /api/products/ean/:code` — slår upp i Open Food Facts, returnerar normaliserat produktförslag (namn, kategori, näringsvärden) utan att spara
2. **Frontend-import** — nytt fält i produktformuläret (eller separat import-flöde): ange EAN → knapp "Hämta" → formuläret förifylls
3. **Alternativt:** kamerabaserad skanning i mobil webbläsare via `BarcodeDetector` API (eller externt bibliotek som `zxing-js`)
4. **Kategorimappning** — Open Food Facts-kategorier är på engelska/franska; en mappningstabell eller fuzzy-matchning mot systemets kategorier behövs
5. **Näringsdata** — om `product.nutriments` finns kan det sparas direkt i Prisma-modellens näringsvärdesfält (redan schema-klara)
**Begränsningar att ha i åtanke:**
- Produkttäckning varierar — svenska dagligvaror är välrepresenterade men inte allt finns
- Produktnamn är ofta på originalspråk; kan behöva redigeras
- Rate limiting: för hög användning rekommenderar Open Food Facts att kontakta dem
### Byta @Post till @Patch på backfill-canonical
Konsekvenser: ⚠️ Bryta ändring.
Frontend anropar /api/products/backfill-canonical med POST från AdminProductList.
Caddy och Next.js API-routes kan ha hårdkodade metoder.
Kräver synkroniserad ändring i backend + alla anropsställen i frontend, annars slutar funktionen fungera.