# TechTitans — Smart Product Creator (Increment 1)

**Goal:** a streamlined, honest product-creation experience with four methods (photo, AI description,
barcode, manual) where every AI path yields an **editable draft requiring explicit approval** before
saving. **Branch:** `techtitans/product-editor-redesign`. **Commits:** frontend `eaf37df`, backend
`b3aabb1` (builds on the product-editor redesign `b92bbb1` / `17546d2`). Not pushed.

This is **Increment 1** of a multi-part build. It delivers the landing + the fully-real natural-language
path + catalogue-first barcode search, and lays honest foundations for photo/vision (Increment 2).

---

## 1. Existing components reused (no parallel catalogue/stock engine)

- **Product create pipeline** — the AI draft is handed to the existing full-page editor
  (`ProductEditorPage` / `ProductFormDrawer`), which remains the single create path: validation
  (`StoreProductRequest` + `ProductTemplateRegistry`), permissions (`products.manage`), transactions,
  variants, inventory setup and media upload are all unchanged. The editor is the **shared approval
  screen**; "Create product" is the explicit approval.
- **AI abstraction** — reused `App\AI\Product` (contracts + config-bound provider + deterministic mock +
  config-gated OpenAI adapter + `AiProductNotConfiguredException` / `AiProductGenerationFailedException`),
  exactly like the description/image generators.
- **Catalogue data** — categories (`categoriesApi`), brands (`brandsApi`), products (`productsApi`) power
  the landing's context and barcode search. Receive-stock routes to the existing Inventory module.
- **Retail product types** — `productKinds` / `product_templates` map the extracted `product_kind` to a
  real template so the editor opens with the right fields.

## 2. New routes & services

**Backend (`b3aabb1`)**
- `POST /api/v1/menu/products/ai/draft` → `AiProductController::draft` (products.manage; persists nothing).
- `App\AI\Product\Contracts\ProductDraftExtractor` + `Mock\MockProductDraftExtractor` (deterministic) +
  `Openai\OpenAiProductDraftExtractor` (real, config-gated) + `Null\NullProductDraftExtractor`.
- `StoreAiDraftRequest` (instruction ≤ 2000 chars; optional context of currency/known categories/brands).
- `config/ai_product.php` → `draft` section; `ProductAiServiceProvider` binds by `ai_product.draft.driver`.

**Frontend (`eaf37df`)**
- Route `ROUTES.productAdd` = `/menu/products/add` → `SmartAddProductPage` (the "Add product" button now
  lands here; quick-add presets and manual entry still open the editor directly).
- `lib/api/menu/productDraft.ts` (`productDraftApi.extract`).
- `ProductFormDrawer` gains an `initialDraft` seed (create only) threaded via `ProductEditorPage` from
  navigation state.

### Request / response — `POST menu/products/ai/draft`
```
Request:  { "instruction": "Create a Logitech keyboard under Computer Accessories for $10, quantity 5",
            "context": { "currency": "CAD", "categories": ["Computer Accessories", ...], "brands": [...] } }
Response: { "data": { "draft": { name, product_kind, brand, model_name, category,
            price:{amount,currency}|null, quantity|null, supplier|null, cost|null, specs|null,
            online_name|null, online_description|null, publish_intent|null,
            uncertain:[...], missing:[...] }, "provider": "mock" } }
```

## 3. The four methods (current status)

| Method | Status | Behaviour |
|---|---|---|
| **Describe with AI** | ✅ Real (mock default / real when configured) | Type or **dictate** (Web Speech API, optional, feature-detected) an instruction → structured draft with "Verify" badges on uncertain fields + missing-field warnings → **Review & create** opens the editor prefilled for approval. |
| **Scan / enter code** | ✅ Real (catalogue-first) | Searches the existing catalogue by SKU/name; offers **View product** / **Receive stock**, or **Create new** on no match. External barcode-DB lookup is only offered when a provider is configured — **never fabricates** brand/specs from an unmatched code. |
| **Manual entry** | ✅ Real | Opens the full editor (the advanced workflow), unchanged. |
| **Take photo / Upload** | ⚠️ Honest placeholder (Increment 2) | Lets you pick/capture a photo and continue to manual entry; **states plainly** that automatic extraction needs a configured vision provider — details are **not** guessed from the image. |

## 4. AI provider configuration & actual availability

- **Draft extraction:** default **deterministic mock** (`AI_PRODUCT_DRAFT_DRIVER` unset → `mock`). Real
  provider: set `AI_PRODUCT_DRAFT_DRIVER=openai` + `AI_PRODUCT_DRAFT_API_KEY` (optional `_MODEL`
  [default `gpt-4o-mini`], `_BASE_URL`, `_TIMEOUT`, `_MAX_INPUT`). An `openai` driver with no key **fails
  closed at 503**; provider/timeout errors → 502. **No real draft provider is configured today**, so the
  mock (honest, flags uncertainty) is what runs — verified live.
- **Secrets are backend-only** (env, never shipped to the client).
- **No auto-publish, no fabricated success:** extraction persists nothing; creation is a separate,
  explicit, permissioned action; unknown price/cost/quantity stay null and are surfaced.

## 5. Screenshots

- Smart Add landing (four methods): `images/smart-add-landing.png`
- Natural-language draft review (Verify badges, "Not provided" for un-stated fields): `images/smart-add-draft.png`

## 6. Tests & results

- **Backend** — `AiProductDraftTest` **5 passed** (full extraction; price never invented when absent;
  instruction required; unauthenticated 401; location-scoped manager 403). `AiProductContentTest` 9 passed.
- **Frontend** — `SmartAddProductPage.test.tsx` **4 passed** (four methods; NL draft → resolved seed handed
  to the editor; missing-price flagged, never invented; barcode catalogue-first search). Editor-prefill
  did not regress the 83 product-editor tests. `tsc` ✓, `eslint` ✓.
- **Live** — `POST /menu/products/ai/draft` returns a correct parsed draft (and a null price + `missing:[price]`
  for a price-less instruction); the landing + draft review render as shown.

## 7. Known limitations / remaining increments

- **Photo / vision analysis (Increment 2):** needs a backend vision contract + mock + config-gated
  multimodal adapter and the upload/camera → draft wiring. Not built yet; the UI is an honest placeholder.
  Requires a vision-capable provider + key to actually extract from images.
- **External barcode lookup:** catalogue-first search is real; an external barcode database needs a
  configured provider (none today).
- **Unified approval screen** currently = the full editor (covers image/name/type/brand/model/category/
  specs/variants/price/quantity/stock/website). **Supplier and cost** are shown in the draft review for
  reference but are **not stored** (no backend field yet) — flagged, not fabricated.
- **Real NL / image / vision providers** are unverified end-to-end (no credentials); adapters + error
  handling are in place and unit-covered with mocks.
- **Serialized inventory (IMEI/condition/cost per unit)** remains unimplemented in the backend and is not
  treated as available.

## Addendum — Photo capture (Choose Photo / Take Photo)

The "Take photo / Upload" method now offers **two distinct options** side by side (stacked on mobile):

- **Choose Photo** — opens the device file picker (JPG / PNG / WebP; computer or mobile gallery).
- **Take Photo** — opens the camera (reusing `CameraModal`, rear-facing) with a **live preview** and
  **Capture → Retake → Use photo**; it shows a clear message when access is **denied / unavailable /
  unsupported**, and **releases the camera stream** on close/capture and on unmount.

After either option: a **large preview** with **Replace photo** / **Retake photo**, **type + size
validation** (JPG/PNG/WebP, ≤ 10 MB), and the vision-provider warning is preserved (no analysis is
claimed). **Use this photo & continue** carries the photo into the editor as the product's pending image
(via a transient `productImageHandoff` store → `CatalogueImageUploader` `initialPendingBlob`), so it flows
through the **existing** create-upload pipeline and is saved as the product image on approval.

- Files: `pages/menu/SmartAddProductPage.tsx` (+ `smart-add.css`, test), `lib/menu/productImageHandoff.ts`
  (new), `components/media/CatalogueImageUploader.tsx` (additive `initialPendingBlob`),
  `components/menu/ProductFormDrawer.tsx` + `pages/menu/ProductEditorPage.tsx` (`initialImage` seed).
- Tests: 4 new (both options shown; chosen image previews + is carried into creation; unsupported type
  rejected; camera-unsupported error). `tsc` ✓, `eslint` ✓; the 87 editor/uploader tests still pass.
- **Real-browser verification** (Playwright): `images/smart-add-photo-options.png`,
  `smart-add-photo-chosen.png`, `smart-add-photo-camera-live.png` (live camera via a fake device),
  `smart-add-photo-in-editor.png` (photo seeded as the editor's product image — note the "Photo ✓" in the
  completion checklist). Camera permission handling verified via the error-state path in unit tests.

## 8. Commit hashes

- Frontend: **`eaf37df`** · Backend: **`b3aabb1`** (branch `techtitans/product-editor-redesign`).
Committed locally only — not pushed or deployed.
