feat(streckkod): okand produkt -> fota forpackning -> spara i delad katalog

Okand streckkod erbjuder nu 'Fota forpackningen' direkt i streckkodsvyn.
Fotot lases av (naring/allergener/innehall) och produkten UPSERTas i den
DELADE products-katalogen kopplat till GTIN (dataSource=user_scanned,
verificationStatus=unverified, createdByUserId=anvandaren) sa nasta person
som skannar samma kod far den direkt. Produkten laggs INTE till i lagret.

Naringsdeklaration-knappen tas bort som egen ingang - flodet vavs in i
streckkodsskanningen. product_package/nutrition_label -> READ_NUTRITION_LABEL
avslutar som 'completed' med result.product (ingen awaiting_confirmation).

- validation: barcode-falt i createScanInput.context
- worker: ny gren i processScanJob som skriver till products + returnerar tidigt
- mobil: inline contributePhoto (fota -> ladda upp -> polla -> visa info)
- scan: ta bort naringsdeklaration-knappen
- i18n: barcode.contributing.* + uppdaterad unknownBody pa 12 sprak

Verifierat: typecheck (validation/worker/api/mobil), worker 23/23, api 104/104,
och repro mot testdatabas (products-upsert, ingen inventory, idempotent aterskann).
This commit is contained in:
Claude
2026-08-20 11:57:58 +00:00
parent 3fa5a878f1
commit 2f596060b4
16 changed files with 256 additions and 22 deletions
+3 -2
View File
@@ -21,7 +21,9 @@ import { useAnalytics } from "@/lib/analytics";
import { scanStarted } from "@app/analytics";
import { FirstScanCoach, useFirstScanCoach } from "@/components/FirstScanCoach";
/** Skanna (spec §4.2): kyl, frys, skafferi, ingredienser, tallrik, kvitto, streckkod, datum, näringsdeklaration. */
/** Skanna (spec §4.2): kyl, frys, skafferi, ingredienser, tallrik, kvitto, streckkod, datum.
* Näringsdeklaration är INGEN egen knapp den vävs in i streckkodsflödet: okänd kod → fota
* förpackningen → produkten läses av och sparas i den delade katalogen (se barcode.tsx). */
const SCAN_TYPES = [
{ type: "fridge", glyph: "🧊", labelKey: "scan.fridge" },
@@ -32,7 +34,6 @@ const SCAN_TYPES = [
{ type: "receipt", glyph: "🧾", labelKey: "scan.receipt" },
{ type: "barcode", glyph: "🏷️", labelKey: "scan.barcode" },
{ type: "expiry_date", glyph: "📅", labelKey: "scan.expiry" },
{ type: "nutrition_label", glyph: "🔬", labelKey: "scan.nutrition" },
] as const;
/**