# Story 5.2: Batch Categorization Job Submission

## Story

**As a** data steward,
**I want** to select products and submit them for AI categorization, then watch the batch progress in real time,
**So that** I can start the categorization process and know when results are ready to review.

## Status

done

## Acceptance Criteria

All ACs met.

## Tasks / Subtasks

- [x] **Task 1: `ProductController::index()`** — paginated (50/page) product table with SKU, MPN, Name, Manufacturer, Status badge, Created At. Multi-select checkboxes. "Submit for Categorization" button that counts selection.

- [x] **Task 2: `CategorizationService`** — `createBatch()`, `spawnCategorizer()` (fires `python/categorize.py` via background process, prefers project venv), `getBatch()`, `getAllBatches()`, `getConfidenceDistribution()`, `productIdsWithExistingPredictions()`

- [x] **Task 3: `CategorizationBatchController`** — `index()`, `create()` (CSRF, PRG, flash), `show()` (terminal banners, confidence distribution, stalled-batch warning), `statusApi()` (JSON). `STATUS_BADGE` constant mirrors import job pattern.

- [x] **Task 4: Views**
  - [x] `src/Views/products/index.php` — sticky action bar appears on first checkbox selection; counts selected; "Submit N products for Categorization" button; two-click confirm when products with existing predictions are selected (first click shows warning + changes button to amber "Confirm — click again").
  - [x] `src/Views/categorization-batches/index.php` — table with live badge polling (10s) for active batches.
  - [x] `src/Views/categorization-batches/show.php` — summary `<dl>`, terminal banners (success/completed_with_errors/failed), confidence distribution card (post-completion), stalled-batch warning (>30min), retry button for failed batches, polling indicator for non-terminal.

- [x] **Task 5: `public/assets/js/categorization-batch-show.js`** — 5s polling, reloads on terminal status.

- [x] **Task 6: Confidence badge CSS** — `.badge-confidence-high/medium/low` appended to `styles.css`.

- [x] **Task 7: Routes + layout** — `/products`, `/categorization-batches`, `/categorization-batches` (POST), `/categorization-batches/{id}`, `/api/categorization-batches/{id}/status`. "Products" added to sidebar. "Predictions" sidebar link updated to `/predictions` placeholder.

## Dev Notes

### Existing-predictions warning (two-click confirm)

The warning is a client-side two-click confirm: first click changes button to amber "Confirm — click again to submit" and sets a `confirmed` flag; second click submits the form. This avoids a modal while still requiring deliberate acknowledgement. The `confirmed` flag resets on any checkbox change.

### Stalled-batch heuristic

`show.php` computes stalled = `!isTerminal && started_at !== null && (now - started_at) > 1800`. No background job needed — it's evaluated on every page load.

### File list

**New files:** `src/Controllers/ProductController.php`, `src/Controllers/CategorizationBatchController.php`, `src/Services/CategorizationService.php`, `src/Views/products/index.php`, `src/Views/categorization-batches/index.php`, `src/Views/categorization-batches/show.php`, `public/assets/js/categorization-batch-show.js`  
**Modified files:** `src/Views/layout.php` (Products link), `public/assets/css/styles.css` (confidence badges), `public/index.php` (routes)
