# Vendor Shopping List — Floating Widget

## Context

The Acquire tab lets users browse vendors and items, but there's no way to bookmark specific vendor items for a shopping trip. Users currently have to remember what they want, or set all waypoints at once with Map All. A **Vendor Shopping List** floating widget lets users curate a list of specific items they want to buy, grouped by zone, then map waypoints from that focused list — like a shopping cart for vendor runs.

**User design decisions:**
- **Grouping**: By Zone (zone headers, vendors within each zone, items under each vendor)
- **Access**: Header button in the Acquire tab (shopping cart icon)
- **Auto-remove**: Collected items automatically removed from the list

---

## Step 1: Store — Add `vendorShoppingList` State

**File**: `Core/HDG_Store.lua`

### 1a. DEFAULT_STATE — add after `shoppingList = {}` (line 64):
```lua
vendorShoppingList = {},  -- { {itemID=N, npcID=N, addedAt=time()} }
```

### 1b. Persistence — add to LoadFromSavedVariables (~line 121) and SaveToSavedVariables (~line 138):
```lua
-- Load:
if HDG_DB.vendorShoppingList then self.state.vendorShoppingList = HDG_DB.vendorShoppingList end
-- Save (add key to HDG_DB table):
vendorShoppingList = self.state.vendorShoppingList,
```

### 1c. CRUD methods (new section after Shopping List Methods):
- `AddToVendorShoppingList(itemID, npcID)` — dedup by itemID+npcID pair, returns bool
- `RemoveFromVendorShoppingList(itemID, npcID)` — remove matching entry
- `ClearVendorShoppingList()` — wipe list
- `IsOnVendorShoppingList(itemID, npcID)` — check membership
- `PruneCollectedFromVendorShoppingList()` — remove entries where `HDG_Data.IsItemCollected(itemID)` is true

### 1d. Dispatch mappings (~line 477):
- `ADD_TO_VENDOR_SHOPPING_LIST` → AddToVendorShoppingList
- `REMOVE_FROM_VENDOR_SHOPPING_LIST` → RemoveFromVendorShoppingList
- `CLEAR_VENDOR_SHOPPING_LIST` → ClearVendorShoppingList

---

## Step 2: Config Constants

**File**: `modules/HDG_Config.lua` — add to FLOATING_WIDGET section (~line 371):
```lua
VENDOR_LIST_WIDTH = 320,
VENDOR_LIST_HEIGHT = 400,
```

---

## Step 3: Shared ZonePctToMap Utility

**File**: `modules/HDG_VendorResolver.lua`

Extract `ZonePctToMap()` as a public function on `HDG_VendorResolver` so both AcquisitionTab and VendorShoppingList can use it (currently duplicated as local in AcquisitionTab):
```lua
function HDG_VendorResolver.ZonePctToMap(uiMapID, x, y)
    if not uiMapID or uiMapID == 0 then return nil end
    if x < -5 or x > 105 or y < -5 or y > 105 then return nil end
    return uiMapID, math.max(0, math.min(1, x / 100)), math.max(0, math.min(1, y / 100))
end
```

Update the local `ZonePctToMap` in `HDG_AcquisitionTab.lua` to delegate to `HDG_VendorResolver.ZonePctToMap()`.

---

## Step 4: New File — `UI/HDG_VendorShoppingList.lua`

**First consumer of `HDG.UI:FloatingWidget()` factory** (Framework.lua:1223).

### 4a. Module structure:
```lua
local addonName, privateDB = ...
local HDG = _G.HousingDecorGuide
HDG_VendorShoppingList = {}
local widget, CONFIG, mainFrame
```

### 4b. Init + Create — uses FloatingWidget factory:
- `HDG_VendorShoppingList:Init(configRef, mainFrameRef)` — store refs
- `HDG_VendorShoppingList:Create()` — lazy create via `HDG.UI:FloatingWidget({...})`
  - Position saved/restored via `HDG.Store:UpdateLayout("vendorShoppingList", ...)` (existing pattern)
  - Title: "Vendor Shopping List" as FontString
  - Header buttons via `widget.CreateHeaderButton()`:
    - **Close** (X icon)
    - **Map All Waypoints** (Waypoint-MapPin-ChatIcon atlas)
    - **Clear All** (transmog-icon-remove atlas)
  - Scrollable list panel via `widget:CreatePanel({ hasScroll = true })`
  - Empty state text when list is empty

### 4c. Refresh — Zone -> Vendor -> Item hierarchy:
1. Call `HDG.Store:PruneCollectedFromVendorShoppingList()` first (auto-remove)
2. Build `zoneMap[zoneName][npcID] = {itemIDs}` from store data
3. Sort zones alphabetically, vendors by name within zone, items by name
4. Render rows:
   - **Zone header**: Full-width accent-colored bar with zone name
   - **Vendor row** (indent 8px): Vendor name + map pin icon if coords valid
   - **Item row** (indent 24px): Item icon/name + remove (X) button on right

Row factories use `HDG_UIHelpers` patterns (FontStrings, hover effects, theme colors).

### 4d. Map All Waypoints:
- Iterate list, dedup by npcID
- Use `HDG_VendorResolver.ZonePctToMap()` for coord conversion
- TomTom waypoint pattern (same as Acquire tab Map All at line 191)

### 4e. Public API:
- `Toggle()` — show/hide, lazy-create, auto-refresh on show
- `IsShown()` — visibility check
- `GetCount()` — for button badge
- `Refresh()` — rebuild content

---

## Step 5: Acquire Tab Integration

**File**: `UI/Tabs/HDG_AcquisitionTab.lua`

### 5a. "List" toggle button in header row (~line 237, next to Map All):
```lua
local shopListBtn = HDG.UI:Button(leftPanel, "List", 38, ACQ.VIEW_TOGGLE_HEIGHT, function()
    HDG_VendorShoppingList:Toggle()
end)
shopListBtn:SetPoint("TOPRIGHT", mapAllBtn, "TOPLEFT", -4, 0)
acquisitionPanel.shopListBtn = shopListBtn
```

Update badge count on RefreshList/Show:
```lua
local count = HDG_VendorShoppingList:GetCount()
acquisitionPanel.shopListBtn:SetText(count > 0 and ("List " .. count) or "List")
```

### 5b. "Add to List" button in vendor detail panel:

In `UpdateDetailPanel_Vendor()` (~line 1095), add a button near the waypoint/map buttons:
- Shows "Add to List" when item not on list
- Shows "On List" (dimmed) when already added
- Calls `HDG.Store:AddToVendorShoppingList(selectedItemID, selectedVendor.npcID)`
- Hidden for non-vendor items (crafted/quest/etc)

### 5c. Init call:

In the tab's `Init()` or `Show()`, call `HDG_VendorShoppingList:Init(CONFIG, mainFrame)`.

---

## Step 6: TOC File

**File**: `HousingDecorGuide.toc` — add after line 56 (`UI\Tabs\HDG_AcquisitionTab.lua`):
```
UI\HDG_VendorShoppingList.lua
```

---

## Critical Files

| File | Change |
|------|--------|
| `Core/HDG_Store.lua` | Add `vendorShoppingList` state, CRUD methods, persistence, Dispatch |
| `modules/HDG_Config.lua` | Add VENDOR_LIST_WIDTH/HEIGHT constants |
| `modules/HDG_VendorResolver.lua` | Extract `ZonePctToMap()` as shared utility |
| `UI/HDG_VendorShoppingList.lua` | **NEW** — Floating widget, zone->vendor->item hierarchy |
| `UI/Tabs/HDG_AcquisitionTab.lua` | "List" button + "Add to List" in vendor detail |
| `HousingDecorGuide.toc` | Add new file |

### Existing patterns reused:
- `HDG.UI:FloatingWidget(config)` — Framework.lua:1223 (first consumer)
- `HDG.Store:UpdateLayout()` — position persistence (Lumber Tracker pattern)
- `HDG_UIHelpers.ClearScrollContent()` — scroll content cleanup
- `HDG_VendorResolver.GetVendorNameByID()` / `GetVendorsForItem()` — vendor data
- `HDG_Data.IsItemCollected()` / `GetCachedItemName()` — collection + names
- TomTom waypoint pattern from Map All button (AcquisitionTab:191)

---

## Verification

1. Deploy and open Acquire tab
2. Confirm "List" button visible next to "Map All"
3. Expand a vendor, select item -> "Add to List" button appears
4. Click "Add to List" -> chat confirms, button shows "On List"
5. Click "List" button -> floating widget opens with zone->vendor->item hierarchy
6. Confirm zone headers, vendor names, item names render correctly
7. Click remove (X) on an item -> removed, widget refreshes
8. Click Map All in widget -> TomTom waypoints set for unique vendors
9. Drag widget to new position -> close/reopen -> position restored
10. Reload UI -> list persisted via SavedVariables
11. Collect a listed item -> reopen widget -> item auto-pruned
12. Click Clear All -> list empties
13. "List N" badge updates in Acquire tab header
