# TSHIRTORDER-1600 — Add Color field on Products + Order

## Yêu cầu gốc

Kèm 2 ảnh mockup (xem `files/mockup-product-detail-color.png` và `files/mockup-order-detail-color.png`).

> **Mockup 1 — Product detail page** (`order.tshirt.se/app/products/{id}/detail`)
> "Add new text field for input color" — mũi tên trỏ vào một text input mới tên **Color**, đặt ở cột phải, ngay dưới checkbox "Woo synka lager".

> **Mockup 2 — Order detail page** (`order.tshirt.se/app/orders/{id}/detail`)
> "Show field here, can edit in order this field" — mũi tên trỏ vào bảng sản phẩm trong đơn hàng, thêm cột **Color** nằm giữa cột STORLEKAR (size) và PRIS EX.MOMS, **edit được trực tiếp trên từng dòng sản phẩm của order**.

Tóm tắt: thêm field "màu sắc" (color) — 1) trên form sản phẩm (giá trị mặc định của product), 2) trên từng dòng sản phẩm trong order (có thể override riêng, khác với màu mặc định của product).

---

## ⚠️ Phát hiện quan trọng: Backend đã có sẵn từ trước

Trước khi code, đã kiểm tra `src/Entity/Product.php`, `src/Entity/OrderProduct.php`, `src/Service/ProductService.php`, `src/Service/OrderService.php`, `src/Repository/OrderProductRepository.php` — **toàn bộ chain backend cho field color đã tồn tại sẵn**, từ migration `Version20240911044314` (2024-09-11), rất lâu trước ticket này:

```sql
ALTER TABLE products ADD color VARCHAR(255) DEFAULT NULL;
ALTER TABLE orders_products ADD product_color VARCHAR(255) DEFAULT NULL;
```

| Layer | File | Trạng thái |
|---|---|---|
| `Product::$color` (get/setColor) | `src/Entity/Product.php:152-154, 745-755` | ✅ Đã có |
| `ProductService::generateItem()` trả `color` | `src/Service/ProductService.php:87` | ✅ Đã có |
| `ProductService::generateUpdate()` ghi `color` | `src/Service/ProductService.php:220-254` | ✅ Đã có — dùng dynamic setter `set{ucfirst($key)}`, không cần whitelist field, tự động hoạt động vì `setColor()` đã tồn tại trên entity |
| `OrderProduct::$productColor` (get/setProductColor) | `src/Entity/OrderProduct.php:79, 424-432` | ✅ Đã có |
| `OrderService::addProducts()` đọc `productColor` từ item hoặc fallback về `product.getColor()` | `src/Service/OrderService.php:789, 925` | ✅ Đã có |
| `OrderProductRepository::getByOrderId()` trả `productColor` trong response order items | `src/Repository/OrderProductRepository.php:319` | ✅ Đã có |

**Kết luận: ticket này thực chất là 100% frontend (React app, không nằm trong repo Symfony này).** Không cần entity mới, không cần migration, không cần sửa Service/Controller. Chỉ cần binding 2 field UI vào 2 API endpoint đã sẵn sàng.

---

## API contract cho frontend

### 1. Product detail — field `color`

- `GET /api/v1/products/{id}/detail` → response có sẵn `color: string|null`
- `PUT /api/v1/products/{id}` → gửi `color` trong payload để lưu (text field tự do, không giới hạn danh sách màu cố định)

### 2. Order detail — field `productColor` (per line item)

- `GET /api/v1/orders/{id}` → mỗi phần tử trong `items[]` có sẵn `productColor: string|null`
  - Khi thêm sản phẩm mới vào order, `productColor` mặc định = `color` của Product tại thời điểm thêm (không tự sync lại nếu Product đổi color sau đó — giống cách `productName`, `productSku` được snapshot).
- `PUT /api/v1/orders/{id}` với payload `items: [...]` → mỗi item object cần có field `productColor` nếu muốn giữ/sửa giá trị màu của dòng đó.

#### ⚠️ Lưu ý bẫy quan trọng cho frontend

`OrderService::addProducts()` xử lý **mọi item gửi lên** (cả item mới lẫn item cũ đang edit qua `orderProductId`) theo cùng 1 logic:

```php
$productColor = isset($item['productColor']) ? trim($item['productColor']) : $product->getColor();
```

→ Nếu frontend submit lại toàn bộ `items[]` của order (pattern thường dùng khi update order) mà **thiếu key `productColor`** trên một item đã có sẵn `orderProductId`, giá trị color của dòng đó sẽ **bị ghi đè về color mặc định hiện tại của Product**, xoá mất giá trị màu đã tuỳ chỉnh riêng cho order đó.

**Bắt buộc**: khi build payload update order, mỗi object trong `items[]` phải luôn include `productColor` (lấy từ state hiện tại của dòng, kể cả khi user không đổi gì), tương tự cách các field khác như `comment`, `sortOrder` đang được resend đầy đủ.

---

## Việc cần làm (frontend — React app, ngoài repo này)

### Mockup 1 — Product detail form
- [ ] Thêm text input "Color" vào form edit product, vị trí theo mockup (cột phải, dưới checkbox "Woo synka lager")
- [ ] Bind giá trị 2 chiều với field `color` từ API `GET/PUT /api/v1/products/{id}`
- [ ] Không cần validate bắt buộc (field nullable ở DB)

### Mockup 2 — Order detail, bảng sản phẩm
- [ ] Thêm cột "Color" vào bảng line items, vị trí giữa STORLEKAR và PRIS EX.MOMS (theo mockup)
- [ ] Cột này là input text edit trực tiếp trên từng dòng (không phải chỉ hiển thị)
- [ ] Khi thêm sản phẩm mới vào order: prefill giá trị từ `product.color` (product được chọn), nhưng cho phép user sửa trước khi lưu
- [ ] Khi submit update order: đảm bảo mỗi item trong payload luôn có `productColor` — xem lưu ý bẫy ở trên

### Test / kiểm tra
- [ ] Tạo/sửa product, nhập color → reload trang → giá trị color hiển thị đúng
- [ ] Thêm product vào order → color tự fill theo color mặc định của product
- [ ] Sửa color riêng cho 1 dòng trong order, save → reload order → giá trị color của dòng đó đúng như đã sửa (không bị revert về color mặc định của product)
- [ ] Sửa các field khác của order (không đụng vào color) → save → color của các dòng cũ không bị mất/ghi đè

---

## Các files liên quan (backend — chỉ để tham khảo, không cần sửa)

| File | Liên quan |
|------|-----------|
| `src/Entity/Product.php` | `$color` field, đã có get/setColor |
| `src/Entity/OrderProduct.php` | `$productColor` field, đã có get/setProductColor |
| `src/Service/ProductService.php` | `generateItem()` line 87, `generateUpdate()` line 220 |
| `src/Service/OrderService.php` | `addProducts()` line 750-940 (đọc/ghi `productColor` cho từng item) |
| `src/Repository/OrderProductRepository.php` | `getByOrderId()` line 297 (select `productColor`) |
| `migrations/Version20240911044314.php` | Migration gốc đã thêm 2 cột này (2024-09-11) |
