# Item Classification Implementation Guide

## Overview
This implementation adds a classification system to the grocery app allowing users to categorize items by type, group, and store zone. The system supports both creating new items with classification and editing existing items.

## Database Schema

### New Table: `item_classification`
```sql
CREATE TABLE item_classification (
  id INTEGER PRIMARY KEY REFERENCES grocery_list(id) ON DELETE CASCADE,
  item_type VARCHAR(50) NOT NULL,
  item_group VARCHAR(100) NOT NULL,
  zone VARCHAR(100),
  confidence DECIMAL(3,2) DEFAULT 1.0,
  source VARCHAR(20) DEFAULT 'user',
  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW()
);
```

**Key Points:**
- One-to-one relationship with `grocery_list` (id is both PK and FK)
- Cascade delete ensures classification is removed when item is deleted
- Classification values are NOT enforced by DB - controlled at app layer
- `confidence`: 1.0 for user input, lower values reserved for future ML features
- `source`: 'user', 'ml', or 'default'

## Architecture

### Backend

**Constants** ([backend/constants/classifications.js](backend/constants/classifications.js)):
- `ITEM_TYPES`: 11 predefined types (produce, meat, dairy, etc.)
- `ITEM_GROUPS`: Nested object mapping types to their valid groups
- `ZONES`: 10 Costco store zones
- Validation helpers: `isValidItemType()`, `isValidItemGroup()`, `isValidZone()`

**Models** ([backend/models/list.model.js](backend/models/list.model.js)):
- `getClassification(itemId)`: Returns classification for an item or null
- `upsertClassification(itemId, classification)`: INSERT or UPDATE using ON CONFLICT
- `updateItem(id, itemName, quantity)`: Update item name/quantity

**Controllers** ([backend/controllers/lists.controller.js](backend/controllers/lists.controller.js)):
- `getClassification(req, res)`: GET endpoint to fetch classification
- `updateItemWithClassification(req, res)`: Validates and updates item + classification

**Routes** ([backend/routes/list.routes.js](backend/routes/list.routes.js)):
- `GET /list/item/:id/classification` - Fetch classification (all roles)
- `PUT /list/item/:id` - Update item + classification (editor/admin)

### Frontend

**Constants** ([frontend/src/constants/classifications.js](frontend/src/constants/classifications.js)):
- Mirrors backend constants for UI rendering
- `getItemTypeLabel()`: Converts type keys to display names

**API Methods** ([frontend/src/api/list.js](frontend/src/api/list.js)):
- `getClassification(id)`: Fetch classification for item
- `updateItemWithClassification(id, itemName, quantity, classification)`: Update item

**Components:**

1. **EditItemModal** ([frontend/src/components/EditItemModal.jsx](frontend/src/components/EditItemModal.jsx))
   - Triggered by long-press (500ms) on any grocery item
   - Prepopulates with existing item data + classification
   - Cascading selects: item_type → item_group (filtered) → zone
   - Validation: Requires item_group if item_type is selected
   - Upserts classification with confidence=1.0, source='user'

2. **ItemClassificationModal** ([frontend/src/components/ItemClassificationModal.jsx](frontend/src/components/ItemClassificationModal.jsx))
   - Shown after image upload in new item flow
   - Required fields: item_type, item_group
   - Optional: zone
   - User can skip classification entirely

3. **GroceryListItem** ([frontend/src/components/GroceryListItem.jsx](frontend/src/components/GroceryListItem.jsx))
   - Long-press detection with 500ms timer
   - Supports both touch (mobile) and mouse (desktop) events
   - Cancels long-press if finger/mouse moves >10px

**Main Page** ([frontend/src/pages/GroceryList.jsx](frontend/src/pages/GroceryList.jsx)):
- Orchestrates all modal flows
- Add flow: Name → Similar check → Image upload → **Classification** → Save
- Edit flow: Long-press → Load classification → Edit → Save

## User Flows

### FEATURE 1: Edit Existing Item

**Trigger:** Long-press (500ms) on any item in the list

**Steps:**
1. User long-presses an item
2. System fetches existing classification (if any)
3. EditItemModal opens with prepopulated data
4. User can edit:
   - Item name
   - Quantity
   - Classification (type, group, zone)
5. On save:
   - Updates `grocery_list` if name/quantity changed
   - UPSERTS `item_classification` if classification provided
   - Sets confidence=1.0, source='user' for user-edited classification

**Validation:**
- Item name required
- Quantity must be ≥ 1
- If item_type selected, item_group is required
- item_group options filtered by selected item_type

### FEATURE 2: Add New Item with Classification

**Enhanced flow:**
1. User enters item name
2. System checks for similar items (80% threshold)
3. User confirms/edits name
4. User uploads image (or skips)
5. **NEW:** ItemClassificationModal appears
   - User selects type, group, zone (optional)
   - Or skips classification
6. Item saved to `grocery_list`
7. If classification provided, saved to `item_classification`

## Data Flow Examples

### Adding Item with Classification
```javascript
// 1. Add item to grocery_list
const response = await addItem(itemName, quantity, imageFile);

// 2. Get item ID
const item = await getItemByName(itemName);

// 3. Add classification
await updateItemWithClassification(item.id, undefined, undefined, {
  item_type: 'produce',
  item_group: 'Fruits',
  zone: 'Fresh Foods Right'
});
```

### Editing Item Classification
```javascript
// Update both item data and classification in one call
await updateItemWithClassification(itemId, newName, newQuantity, {
  item_type: 'dairy',
  item_group: 'Cheese',
  zone: 'Dairy Cooler'
});
```

## Backend Request/Response Shapes

### GET /list/item/:id/classification
**Response:**
```json
{
  "item_type": "produce",
  "item_group": "Fruits",
  "zone": "Fresh Foods Right",
  "confidence": 1.0,
  "source": "user"
}
```
Or `null` if no classification exists.

### PUT /list/item/:id
**Request Body:**
```json
{
  "itemName": "Organic Apples",
  "quantity": 5,
  "classification": {
    "item_type": "produce",
    "item_group": "Organic Produce",
    "zone": "Fresh Foods Right"
  }
}
```

**Validation:**
- Validates item_type against allowed values
- Validates item_group is valid for the selected item_type
- Validates zone against allowed zones
- Returns 400 with error message if invalid

**Response:**
```json
{
  "message": "Item updated successfully"
}
```

## Setup Instructions

### 1. Run Database Migration
```bash
psql -U your_user -d your_database -f backend/migrations/create_item_classification_table.sql
```

### 2. Restart Backend
The backend automatically loads the new classification constants and routes.

### 3. Test Flows

**Test Edit:**
1. Long-press any item in the list
2. Verify modal opens with item data
3. Select a type, then a group from filtered list
4. Save and verify item updates

**Test Add:**
1. Add a new item
2. Upload image (or skip)
3. Verify classification modal appears
4. Complete classification or skip
5. Verify item appears in list

## Validation Rules Summary

1. **Item Type → Item Group Dependency**
   - Must select item_type before item_group becomes available
   - Item group dropdown shows only groups for selected type

2. **Required Fields**
   - When creating: item_type and item_group are required
   - When editing: Classification is optional (can edit name/quantity only)

3. **No Free-Text**
   - All classification values are select dropdowns
   - Backend validates against predefined constants

4. **Graceful Handling**
   - Items without classification display normally
   - Edit modal works for both classified and unclassified items
   - Classification is always optional (can be skipped)

## State Management (Frontend)

**GroceryList.jsx state:**
```javascript
{
  showEditModal: false,
  editingItem: null,  // Item + classification data
  showClassificationModal: false,
  classificationPendingItem: {
    itemName, 
    quantity, 
    imageFile
  }
}
```

## Long-Press Implementation Details

**Timing:**
- Desktop (mouse): 500ms hold
- Mobile (touch): 500ms hold with <10px movement threshold

**Event Handlers:**
- `onTouchStart`: Start timer, record position
- `onTouchMove`: Cancel if movement >10px
- `onTouchEnd`: Clear timer
- `onMouseDown`: Start timer
- `onMouseUp`: Clear timer
- `onMouseLeave`: Clear timer (prevent stuck state)

## Future Enhancements

1. **ML Predictions**: Use confidence <1.0 and source='ml' for auto-classification
2. **Bulk Edit**: Select multiple items and apply same classification
3. **Smart Suggestions**: Learn from user's classification patterns
4. **Zone Optimization**: Suggest optimal shopping route based on zones
5. **Analytics**: Most common types/groups, zone coverage

## Troubleshooting

**Issue:** Classification not saving
- Check browser console for validation errors
- Verify item_type/item_group combination is valid
- Ensure item_classification table exists

**Issue:** Long-press not triggering
- Check that user has editor/admin role
- Verify onLongPress prop is passed to GroceryListItem
- Test on both mobile (touch) and desktop (mouse)

**Issue:** Item groups not filtering
- Verify item_type is selected first
- Check that ITEM_GROUPS constant has entries for the selected type
- Ensure state updates are triggering re-render

## Files Modified/Created

### Backend
- ✅ `backend/constants/classifications.js` (NEW)
- ✅ `backend/models/list.model.js` (MODIFIED)
- ✅ `backend/controllers/lists.controller.js` (MODIFIED)
- ✅ `backend/routes/list.routes.js` (MODIFIED)
- ✅ `backend/migrations/create_item_classification_table.sql` (NEW)

### Frontend
- ✅ `frontend/src/constants/classifications.js` (NEW)
- ✅ `frontend/src/api/list.js` (MODIFIED)
- ✅ `frontend/src/components/EditItemModal.jsx` (NEW)
- ✅ `frontend/src/components/ItemClassificationModal.jsx` (NEW)
- ✅ `frontend/src/components/GroceryListItem.jsx` (MODIFIED)
- ✅ `frontend/src/pages/GroceryList.jsx` (MODIFIED)
- ✅ `frontend/src/styles/EditItemModal.css` (NEW)
- ✅ `frontend/src/styles/ItemClassificationModal.css` (NEW)

## API Summary

| Method | Endpoint | Auth | Description |
|--------|----------|------|-------------|
| GET | `/list/item/:id/classification` | All roles | Get item classification |
| PUT | `/list/item/:id` | Editor/Admin | Update item + classification |

## Database Operations

**Upsert Pattern:**
```sql
INSERT INTO item_classification (id, item_type, item_group, zone, confidence, source)
VALUES ($1, $2, $3, $4, $5, $6)
ON CONFLICT (id) 
DO UPDATE SET 
  item_type = EXCLUDED.item_type,
  item_group = EXCLUDED.item_group,
  zone = EXCLUDED.zone,
  confidence = EXCLUDED.confidence,
  source = EXCLUDED.source
RETURNING *;
```

This ensures we INSERT if no classification exists, or UPDATE if it does.
