# Plan: Batch Add Products — Photo Upload + Category Dropdown Fix

## Goal
Two fixes for `resources/js/Pages/BatchAddProducts.vue`:
1. **Optional photo upload per product leaf** (new products only) — tap an area to pick a photo, upload immediately via `useFileUpload`, and include the returned hashkey in the submit payload.
2. **Category field converts to a `<select>` dropdown** — currently uses `<input type="text" list="leaf-categories">` which behaves as plain text on mobile. Replace with a `<select>` populated from the `categories` ref (same data already loaded from `/Products/New/Category/Datalist`). Keep "None / other" as the first empty option so the field stays optional.

---

## Context

### Key files
- **Frontend**: `resources/js/Pages/BatchAddProducts.vue` — all changes happen here
- **Composable**: `resources/js/composables/useFileUpload.js` — `uploadFile(file)` → POST `/File/Upload/ProductMarket` → returns `{ hashkey }`. Already used in `CreateProductStoreOwner.vue`.
- **Backend**: `app/Http/Controllers/Market/BatchController.php` → `batchCreateProducts()` — needs to accept `photourl` and pass it to `Product::create()`.
- **Product model**: `app/Models/Market/Product.php` — `photourl` is already a cast array field (line 27/59).

### Category data
`fetchCategories()` already calls `POST /Products/New/Category/Datalist` → `{ success: true, categories: ['Vegetables', 'Fruits', ...] }`. The raw `categories` ref holds a string array. The `<datalist>` approach already uses it; replacing with `<select>` just maps the same array to `<option>` elements.

### Photo upload flow (existing pattern from CreateProductStoreOwner.vue)
```js
import { useFileUpload } from '../composables/useFileUpload.js'
const { uploadFile, uploadError } = useFileUpload({ category: 'ProductMarket' })

// on file input change:
const result = await uploadFile(file)
if (result?.hashkey) leaf.photoHash = result.hashkey
```

### Leaf state shape (current makeLeaf)
```js
const makeLeaf = () => ({
  source: 'new',
  product_hash: '',
  linked: null,
  name: '',
  price: 0,
  available: 0,
  unitname: 'pcs',
  description: '',
  category: '',
  subcategory: '',
  barcode: '',
})
```
Add `photoHash: ''` and `photoUploading: false` to this shape.

### Backend payload change
In `saveProducts()`, for `source === 'new'` leaves add:
```js
photourl: p.photoHash ? [p.photoHash] : [],
```
In `BatchController::batchCreateProducts()`, add to the validator for `source === 'new'`:
```php
'photourl' => 'nullable|array',
'photourl.*' => 'nullable|string',
```
And in `Product::create([...])` add:
```php
'photourl' => $productData['photourl'] ?? [],
```

---

## Step-by-step implementation

### Step 1 — Add `photoHash` + `photoUploading` to leaf state
In `makeLeaf()`:
```js
const makeLeaf = () => ({
  // ...existing fields...
  photoHash: '',
  photoUploading: false,
})
```

### Step 2 — Import `useFileUpload` and wire a single uploader instance
Because each leaf uploads independently, instantiate `useFileUpload` once and use a helper:
```js
import { useFileUpload } from '../composables/useFileUpload.js'
const { uploadFile } = useFileUpload({ category: 'ProductMarket' })

const handleLeafPhoto = async (index, event) => {
  const file = event.target.files?.[0]
  if (!file) return
  products.value[index].photoUploading = true
  const result = await uploadFile(file)
  products.value[index].photoUploading = false
  if (result?.hashkey) products.value[index].photoHash = result.hashkey
}

const removeLeafPhoto = (index) => {
  products.value[index].photoHash = ''
}
```

### Step 3 — Add photo upload UI inside the `source === 'new'` template block
Place above the description field, after the Barcode row:

```html
<!-- Photo (optional) -->
<label class="form-label small fw-bold text-muted mb-1 mt-1">Photo</label>
<div class="d-flex align-items-center gap-2">
  <label
    v-if="!product.photoHash"
    class="btn btn-outline-secondary btn-sm rounded-pill flex-grow-1"
    :class="{ disabled: product.photoUploading }"
    :for="`photo-input-${index}`"
    style="cursor:pointer;"
  >
    <span v-if="product.photoUploading">
      <LoadingSpinner size="small" class="me-1" /> Uploading…
    </span>
    <span v-else>
      <i class="fas fa-camera me-1"></i> Add Photo
    </span>
  </label>
  <div v-else class="d-flex align-items-center gap-2 flex-grow-1">
    <img
      :src="`/RequestData/File/${product.photoHash}`"
      class="rounded-2 border"
      style="width:48px;height:48px;object-fit:cover;"
      alt="Product photo"
    />
    <button
      class="btn btn-link btn-sm text-danger p-0"
      @click="removeLeafPhoto(index)"
      title="Remove photo"
    >
      <i class="fas fa-times-circle"></i>
    </button>
  </div>
  <input
    :id="`photo-input-${index}`"
    type="file"
    accept="image/*"
    class="d-none"
    @change="(e) => handleLeafPhoto(index, e)"
  />
</div>
```

Use `/RequestData/File/${hashkey}` for the preview (same path used by `FileList::resolvedUrl()` fallback).

### Step 4 — Replace category `<input list>` with `<select>`
Replace:
```html
<input
    v-model="product.category"
    type="text"
    list="leaf-categories"
    class="form-control form-control-sm"
    placeholder="e.g. Vegetables"
>
```
With:
```html
<select v-model="product.category" class="form-select form-select-sm">
  <option value="">— Category —</option>
  <option v-for="cat in categories" :key="cat" :value="cat">{{ cat }}</option>
</select>
```
Also remove the `<datalist id="leaf-categories">` element at the bottom of the template since it is no longer needed.

### Step 5 — Include `photourl` in the save payload
In `saveProducts()`, update the `source === 'new'` branch of the payload map:
```js
{
  source: 'new',
  name: p.name,
  price: p.price,
  available: p.available,
  unitname: p.unitname,
  description: p.description,
  category: p.category,
  subcategory: p.subcategory,
  barcode: p.barcode,
  photourl: p.photoHash ? [p.photoHash] : [],
}
```

### Step 6 — Update `BatchController::batchCreateProducts()` to persist photourl
In `app/Http/Controllers/Market/BatchController.php`, in the `source === 'new'` validator rules, add:
```php
'photourl' => 'nullable|array',
'photourl.*' => 'nullable|string',
```
In `Product::create([...])`, add:
```php
'photourl' => $productData['photourl'] ?? [],
```

---

## Definition of Done checklist
- [ ] New leaf cards show an "Add Photo" button that opens a file picker
- [ ] Selecting a photo uploads it and shows a thumbnail preview with a remove ×
- [ ] Loading spinner shown during upload; button disabled while uploading
- [ ] `photoHash` is included in the save payload as `photourl: [hash]`
- [ ] Backend accepts and stores `photourl` on the new product
- [ ] Category field is a `<select>` dropdown populated with categories from the API
- [ ] Selecting a category from the dropdown sets `product.category` correctly
- [ ] `<datalist id="leaf-categories">` removed from template
- [ ] Existing fields (subcategory, barcode, description) unchanged
- [ ] Dark-mode styles still apply (form-select already covered by existing dark-mode scoped rule)
- [ ] `source === 'existing'` leaves are unaffected (no photo upload shown, category not editable)
- [ ] Build passes (`npm run build`)
