# Inventory Display Redesign

## Overview

Redesign the `/inventory` command from a basic embed listing to a polished Components V2 experience with rarity indicators, paginated list view, item detail view with artwork, and inline item management actions.

## Rarity Emoji Mapping

Add a `squareEmoji` field to `RARITY_CONFIG` in `shared/lib/rarity.ts`:

| Rarity | squareEmoji | Existing emoji | Color hex |
|--------|-------------|----------------|-----------|
| C      | 🟤          | 📦             | 0x95A5A6  |
| R      | 🔵          | 📦             | 0x3498DB  |
| SR     | 🟣          | ✨             | 0x9B59B6  |
| SSR    | 🟡          | 🌟             | 0xF1C40F  |

Non-item rarities (CURRENCY, XP, NOTHING) do not get square emojis. The existing `emoji` field remains unchanged (used by lootbox results).

## List View

The `/inventory [user]` command renders a Components V2 message:

1. **Header** — `TextDisplayBuilder`: `# 📦 {username}'s Inventory` with subtitle showing total item count.
2. **Separator**
3. **Item rows** (5 per page) — Each item is a `TextDisplayBuilder` line: `{squareEmoji} **{Item Name}** — {Rarity Label} · {Type} · ×{quantity}`
4. **Separator**
5. **Select menu** — `StringSelectMenuBuilder` populated with the 5 items on the current page. Placeholder: "Select an item for details". Each option shows item name and rarity label.
6. **Navigation row** — `ActionRowBuilder`: `◀ Previous` (disabled on page 1), disabled `Page X/Y` indicator button, `Next ▶` (disabled on last page).

**Container:** `ContainerBuilder` with accent color from the highest-rarity item on the current page.

**Sorting:** Items sorted by rarity descending (SSR → SR → R → C), then alphabetically within the same rarity.

**Empty state:** If inventory is empty, show: "No items yet. Visit the shop or complete quests to earn items!"

**Collector:** `createMessageComponentCollector` with 2-minute idle timeout. On timeout, disable all interactive components.

## Detail View

Shown when a user selects an item from the dropdown or uses `/inventory view <item>`:

1. **Header section** — `SectionBuilder`:
   - `TextDisplayBuilder`: `{squareEmoji} **{Item Name}**` with subtitle `-# {Rarity Label} · {Type}`
   - `ThumbnailBuilder` with the item's `iconUrl`
2. **Artwork** — `MediaGalleryBuilder` displaying the item's `imageUrl`
3. **Description** — `TextDisplayBuilder` with the item's `description`
4. **Separator**
5. **Stats row** — `TextDisplayBuilder`: `Owned: **×{quantity}**` and `Value: **{price} 🪙**` (or "Not tradeable" if price is null)
6. **Action buttons** — `ActionRowBuilder`:
   - `◀ Back` (primary) — always shown, returns to list view at the same page
   - `🧪 Use` (success) — only shown if **viewer is the owner** AND item type is CONSUMABLE with effects defined
   - `🗑 Discard` (danger) — only shown if **viewer is the owner**

**Container:** `ContainerBuilder` with accent color matching the item's rarity color.

### Ownership Protection

The command tracks two IDs: `viewerId` (who ran the command) and `ownerId` (whose inventory is displayed). When `viewerId !== ownerId`, the inventory is **read-only**:
- The detail view only shows the Back button (no Use or Discard).
- The interaction handler validates `viewerId === ownerId` before executing `useItem` or `removeItem`, as a server-side guard even if the buttons were somehow rendered.

### Use Button Flow

Calls `inventoryService.useItem()` and shows the result inline. Then returns to the detail view with updated quantity. If quantity reaches 0, returns to the list view.

### Discard Flow

1. Clicking `🗑 Discard` replaces the action row with a confirmation: "Discard 1× {Item Name}?" with `Confirm` (danger) and `Cancel` (secondary) buttons.
2. On confirm: calls `inventoryService.removeItem(userId, itemId, 1)`, returns to detail view with updated quantity. If quantity reaches 0, returns to list view.
3. On cancel: returns to the normal detail view action buttons.

## `/inventory view <item>` Subcommand

Adds a `view` subcommand with a required `item` string option that has autocomplete. Autocomplete queries the user's inventory items (reusing the pattern from `getAutocompleteItems`). Goes directly to the detail view. The Back button returns to the full paginated list at page 1.

## Item Selection Entry Points

Two ways to reach the detail view:
- **Select menu dropdown** on the inventory list — for browsing
- **`/inventory view <item>`** subcommand — for direct access when the user knows the item name

Both render the same detail view.

## Interaction Custom IDs

All custom IDs include the invoking user's ID to prevent other users from interacting:

| Custom ID | Purpose |
|-----------|---------|
| `inv_select_{userId}` | Item select menu |
| `inv_prev_{userId}` | Previous page button |
| `inv_next_{userId}` | Next page button |
| `inv_back_{userId}` | Back to list from detail |
| `inv_use_{userId}` | Use item button |
| `inv_discard_{userId}` | Discard item button |
| `inv_discard_confirm_{userId}` | Confirm discard |
| `inv_discard_cancel_{userId}` | Cancel discard |

## File Changes

### Modified

- **`shared/lib/rarity.ts`** — Add `squareEmoji` field to `RARITY_CONFIG` entries for C, R, SR, SSR.
- **`bot/commands/inventory/inventory.ts`** — Rewrite to CV2 with pagination collector. Add `view` subcommand with autocomplete. Command setup and collector logic live here.
- **`bot/modules/inventory/inventory.view.ts`** — Replace `getInventoryEmbed` with `getInventoryListMessage` (builds the paginated CV2 list) and add `getItemDetailMessage` (builds the detail CV2 view). `getLootboxResultMessage` is untouched.

### New

- **`bot/modules/inventory/inventory.interaction.ts`** — Handles all inventory interaction routing: select menu item selection, pagination buttons, back navigation, use item, discard + confirmation flow.

### Unchanged

- `shared/modules/inventory/inventory.service.ts` — Already provides `getInventory`, `useItem`, `removeItem`, `getAutocompleteItems`.
- Database schema — All required fields (`iconUrl`, `imageUrl`, `description`, `rarity`, `type`, `price`) already exist on the items table.

## Pagination Details

- **Items per page:** 5
- **Page calculation:** `totalPages = Math.ceil(items.length / 5)`
- **Page clamping:** `safePage = Math.min(page, totalPages - 1)` to handle items being consumed while browsing
- **Collector timeout:** 2 minutes idle, matching the quest system pattern
- **On timeout:** Edit message to disable all buttons and the select menu
