# 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 |
| Import-microservice (importer-api, backend-till-backend) | ✅ Klart (2026-04-30) |
| Kvittoimport — granskningsflöde i Flutter (edit, merge, destination) | ✅ Klart (2026-05-01) |
| 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 | ✅ Ersatt — seed innehåller nu enbart kategorier (2026-05-02) |
| Användarspecifika produkter (UserProduct) | ✅ Klart (2026-05-02) — `Product.ownerId` obligatorisk, globala produkter borttagna |
| 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-05-02

### Nyheter och förbättringar
- **Produkter user-scoped — ny databasarkitektur (2026-05-02)** — `Product.ownerId` är nu obligatorisk (non-nullable). Alla globala seed-produkter är borttagna. Varje produkt ägs av en enskild användare och raderas vid kontoradering (CASCADE). `seed_all.sql` innehåller nu enbart kategorier. Kvittoimportens matchning filtrerar på `ownerId = userId` från JWT. Se TEKNISK_BESKRIVNING.md för fullständig beskrivning.
- **Kategorier utökade (2026-05-02)** — Nya L2/L3-noder: `Bröd & Kakor > Kondis & fika > Kaffebröd` (wienerbröd, donuts, munkar m.m.) och `Dryck > Te & choklad > Te` (chai, vanilla chai, ceylon te m.m.). Nya L3-noder under `Mejeri, ost & ägg > Allergi mejeri`: Laktosfri mjölk, Filmjölk & Yoghurt, Kvarg & Cottage cheese, Matfett, Allergi matlagning.
- **Regelbaserad kategoridetektion utökad (2026-05-02)** — `ruleBasedCategorySuggestion()` täcker nu Te (te, tea, chai, tepas) och Kaffebröd (wienerbröd, donut, munk, croissant, kanelbulle, bakelse, semla m.fl.) utöver befintliga mejeri-regler.
- **AI-guardrail (2026-05-02)** — `AiService.suggestCategory()` remappar nu `low`/`medium`-konfidenspoäng till L1-föräldern istället för att returnera potentiellt fel L2/L3-kategori.
- **Förbättrad produktmatchning (2026-05-02)** — `findWordMatch` normaliserar nu diakritik (ä→a, ö→o, å→a) före jämförelse och tillåter enstaka stark partiell matchning för ord ≥5 tecken (löser t.ex. "vispgrädde" → produkt "grädde").
- **Kategorisuggest för matchade produkter (2026-05-02)** — `matchProducts()` läser nu in `categoryRef` för matchade produkter och sätter `categorySuggestion` direkt. `enrichWithAiCategories()` körs för alla items utan `categorySuggestion`, inte bara ej matchade.
- **Kvittoimport Fas 6b klar (2026-05-01)** — Flutter-granskningsflödet färdigt: per-rad checkbox, redigeringsdialog med destination-väljare (Inventarie/Baslager), merge-förhandsvisning, parallell laddning av inventarie och baslager, snackbar med separat räkning.
- **Kvittoimport Fas 6c klar (2026-05-01)** — Separering av AI-chip och produktsuggestions-chip, produktnamns-normalisering, och validering av AI-kategorier.
- **Microservice-importer integrerad (2026-04-30)** — All import-logik (URL-skrapning, OCR, PDF-parsning, AI-kvittoparsning) delegeras nu till `importer-api` som körs som intern Docker-tjänst. `recipe-api` behåller produktmatchning och AI-kategorisering. Se [migrering-MSI.md](migrering-MSI.md) för detaljer.
- **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.
- **Flutter-parity** — Matplan, inventarie, baslager och receptflöden är nu fullt migrerade till Flutter med user-scope och robust felhantering.

### Kända begränsningar
- Kvittoimport: användare utan egna produkter får inga produktmatchningar — enbart kategorisuggestioner via regler/AI (förväntat beteende, byggs upp allt eftersom).
- `ReceiptAlias` är ännu inte user-scoped (alias är fortfarande globala — se aliasstrategin nedan).
- `receipt_import_tab.dart` (~1 400 rader) och `swipeable_inventory_tile.dart` är ännu inte lokaliserade.
- Vissa adminfunktioner och avancerad AI-integration är planerade men ej migrerade.

---

## Nästa steg

1. Bygg upp UI-flöde för att skapa/spara egna produkter vid kvittobekräftelse (POST /products med `ownerId` från JWT).
2. Inför user-scope för `ReceiptAlias` (lägg till `userId`, unika index, admin-godkänd global fallback).
3. Implementera automatisk alias-inlärning vid manuell korrigering i importflödet.
4. Kvittoimport steg 2: persistenta förpackningsfält i inventarie (packCount, packSizeQuantity, packSizeUnit) + visning/redigering i inventory-UI.
5. Lokalisera `receipt_import_tab.dart` och `swipeable_inventory_tile.dart`.
6. Smoke-test på testdomän och avstämning.
7. Planera och påbörja avancerad AI-integration och EAN-skanning.
8. AI-kategorisering: kandidatbegränsning före AI (narrowing till relevant gren/kandidatlista istället för hela kategoriträdet).
9. AI-kategorisering: egen confidence-score i backend (regel/lexikal signal + AI-signal), inte enbart modellens confidence.
10. AI-kategorisering: logga användarkorrigeringar och bygg feedbackloop för nya regler/synonymer.
11. AI-kategorisering: utvärdera alternativ modell för kategorisering med A/B-test på historiska kvittorader.

## Beslut 2026-05-02 - Aliasstrategi för kvittoimport

- Alias ska vara hybrid: user-scope som standard, globalt alias endast via admin-verifiering.
- Syfte: minimera felträffar mellan användare och samtidigt bygga en kvalitetssäkrad global kunskapsbas.
- Tekniskt nästa steg i backend:
- Lägg till user-scope för `ReceiptAlias` (t.ex. `userId` + relevanta unika index).
- Behåll global alias som fallback för admin-godkända mappningar.
- Tekniskt nästa steg i klient:
- Spara manuell korrigering som alias (default: user-scope).
- Eventuell admin-toggle för att spara alias globalt.

---

## 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å 1–3) 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')` på `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.
