# Repair Intake — Category & Brand Searchable Dropdowns (inline Add New)

**Date:** 2026-11-07 · **Branch:** `techtitans/repair-r1` · Local commits only (no push). No change to unrelated repair functionality; device records still store category/brand as **name strings** (existing devices unaffected).

## What changed
In **New Repair → Step 1 → Register a new device**, Category and Brand are now **searchable dropdowns with inline "+ Add New"** instead of plain text fields.

## Backend — reuse, no duplicate systems
- **Brand** reuses the existing tenant **brand catalogue** (`brands` table + `BrandService`, slug dedup). New **repairs-gated** thin endpoints delegate to `BrandService` so repair staff (who may not hold `products.manage`) can list/add brands without a competing brand store:
  - `GET /api/v1/repairs/brands` (repairs.view) · `POST /api/v1/repairs/brands` (repairs.manage, `resolve.location`, `X-Request-Id`). A case-different add returns the SAME brand.
  - `app/Http/Controllers/Api/V1/Repairs/RepairBrandController.php`, `app/Http/Requests/Api/V1/Repairs/StoreRepairBrandRequest.php`.
- **Category** — R1 had no device-category config, and the product `categories` tree is a different concept, so a dedicated tenant config was added following the R1 config pattern:
  - Table `repair_device_categories` (`2026_11_07_120000_...`) — UUID + `tenant_id`, `name`, **stored generated `normalized_name = lower(trim(name))`**, unique `(tenant_id, normalized_name)` → **case-/space-insensitive uniqueness**. Model `RepairDeviceCategory`.
  - `RepairConfigService::deviceCategories()` + `createDeviceCategory()` (dedup **returns** the existing row — inline add is idempotent). `RepairConfigController::indexDeviceCategories/storeDeviceCategory`. Request `StoreRepairDeviceCategoryRequest`.
  - `GET /api/v1/repairs/config/device-categories` (repairs.view) · `POST` (repairs.manage, `X-Request-Id`).
  - Defaults (Phone, Tablet, Laptop, Desktop, Smartwatch, Games console, Other) seeded via `RepairDefaultsProvisioner` (idempotent); seeded into both existing `techpos` tenants.
- Writes are tenant-scoped, audited (`repairs.device_category_configured`), and gated so intake staff can add without `repairs.configure`. Migration applied to `techpos` (additive). Tests: `tests/Feature/Repairs/RepairLookupApiTest.php` — **6 pass** (list, create, case/space dedup, manage-required, brand reuse+dedup, tenant isolation). Pint + PHPStan clean.

## Frontend — `LookupSelect`
- New `src/components/repairs/LookupSelect.tsx`: searchable combobox with an always-visible **Add New** / "Create "…"" affordance, `loading`/`error`+retry/`empty` states, keyboard nav + ARIA (`role=combobox/listbox/option`, arrows/Enter/Escape), menu z-indexed above the card + sticky bar (no clipping). The **value is the name string**, so the device-create payload is unchanged and a saved device's existing category/brand that isn't in the list still displays and stays valid.
- `src/lib/api/repairs.ts`: `deviceCategories()/createDeviceCategory()/brands()/createBrand()` (+ types). `NewRepairPage.tsx` wires the two selects into the register-new-device grid (Category · Brand · Model). AI draft brand/category map into the controls as the current value (never auto-created). State lives on the page, so selections persist across wizard steps.
- Frontend checks: **33 Vitest tests pass** (8 files incl. new `NewRepairLookups.test.tsx`), `tsc` clean, lint 0 errors, build succeeds.

## Browser verification (live `techpos`, desktop + mobile)
Screenshots in `docs/techtitans/screenshots/repair-lookups/`:
- `02-category-open` — Category dropdown lists the 7 seeded options, not clipped.
- `03-brand-create-affordance` / `04-brand-created-selected` — typing "Verifly" surfaces "+ Create "Verifly"", which creates + auto-selects.
- `05-category-dedup-typed` — case-insensitive filtering to the existing option.
- `07-step1-after-roundtrip` — Category/Brand/Model **persist** after Continue → Back.
- `01-mobile-device-form` / `02-mobile-category-open` — responsive mobile layout + dropdown.

Verified: select existing, create new, case/space dedup, saved-device display, back/continue persistence, AI mapping (no auto-create), desktop + mobile.
