feat: separate AI and product suggestion chips, normalize product names, and validate AI categories
This commit is contained in:
@@ -2,6 +2,47 @@
|
||||
|
||||
Viktigt att komma ihåg vid implementering av nya funktioner och kodning är att inte använda Windows-sökvägar. Använd inte `c:/dev/recipe-app/...` eftersom bygg- och testmiljön är på en remote Ubuntu-server. Utveckling sker lokalt och test samt drift sker på remote server. Säkerställ att inga absoluta Windows-sökvägar används i koden, för att stödja bygg och drift på Linux/Ubuntu.
|
||||
|
||||
## Senaste ändringar (2026-05-01, session 3)
|
||||
|
||||
### Separering av AI-chip och produktsuggestions-chip
|
||||
|
||||
**Problem:** AI-chipet visade felaktigt produktnamnsförslag som om de vore kategoriförslag, vilket skapade förvirring när användaren såg "AI-förslag: Dryck Multivitamin" (ett produktnamn) istället för en kategori.
|
||||
|
||||
**Lösning:**
|
||||
- **Blå chip** "Förslag: [produktnamn]" — när systemet hittat en trolig produkt via ordmatchning (inga AI-anrop inblandade). Klick väljer produkten.
|
||||
- **Grön chip** "AI-kategori: [kategoriväg]" — när AI:n föreslagit en kategori från databasen. Klick öppnar produktpickern filtrerad på den kategorin.
|
||||
|
||||
**Kodändringar:**
|
||||
- `aiLabel` beräknas enbart från `categorySuggestionName`/`categorySuggestionPath` (kategoriförslag).
|
||||
- Nytt fält `suggestedProductLabel` för produktsuggestions-chip.
|
||||
- Separata villkor och UI-block för de två chipen i `_EditDialogState.build()`.
|
||||
|
||||
### Produktnamns-normalisering
|
||||
|
||||
**Problem:** Kvittonamn i VERSALER (t.ex. "APRIKOSMARMELAD 284G") såg oprofessionella ut i UI:n.
|
||||
|
||||
**Lösning:** Ny funktion `_normalizeProductName()` som tillämpar smarta regler:
|
||||
- Token med `/` (förkortningar) lämnas i versaler: `KY/KAL/LE/TO`
|
||||
- Token som börjar med siffra (mängd/storlek) görs till gemener: `284G` → `284g`, `12X85G` → `12x85g`
|
||||
- Övriga token: första bokstav versal, resten gemen: `APRIKOSMARMELAD` → `Aprikosmarmelad`, `JUICE TROPISK` → `Juice Tropisk`
|
||||
|
||||
**Implementering:**
|
||||
- Top-level-funktion i `receipt_import_tab.dart`
|
||||
- Tillämpas när "Ny produkt"-fältet prefylls: `_newProductNameCtrl.text = _normalizeProductName(widget.current.productName ?? widget.item.rawName)`
|
||||
|
||||
### AI-kategorisering — validering i backend
|
||||
|
||||
**Problem:** Användaren rapporterade att AI föreslog kategorin "Dryck Multivitamin", som inte fanns i databasen.
|
||||
|
||||
**Undersökning:**
|
||||
- Backend-AI:n (`ai.service.ts`) validerar redan att `categoryId` finns i `categories`-listan och faller tillbaka på "Övrigt" om inte.
|
||||
- Problemet var att frontend visade produktnamnsförslag som om de vore kategoriförslag.
|
||||
|
||||
**Lösning:**
|
||||
- Separering av chipen (se ovan) gör det tydligt att AI-kategoriförslag alltid kommer från databasen.
|
||||
|
||||
---
|
||||
|
||||
## Senaste ändringar (2026-05-01, session 2)
|
||||
|
||||
### Tvåstegs-picker: Kategori → Produkt
|
||||
@@ -58,7 +99,7 @@ Importfliken laddar globala och privata produkter parallellt via `Future.wait` o
|
||||
User-scope-principen dokumenterades formellt i båda tekniska beskrivningarna (2026-05-01). Privata produkter är det första exemplet på mönstret för resurser som är varken globala (alla ser dem) eller fullt user-owned (bara ägaren ser dem):
|
||||
- `Product.isPrivate = true` + `Product.ownerId = userId`
|
||||
- `normalizedName`-prefix undviker databaskollision med globala produkter
|
||||
- Migration: `20260501000000_add_product_is_private`
|
||||
- Migration: `20260501000000_add_product_is_private
|
||||
|
||||
## Senaste ändringar (2026-05-01, session 1)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user