docs: add inventory display redesign spec

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
syntaxbullet
2026-03-28 16:56:45 +01:00
parent 47ea6d8620
commit 289044e26f

View File

@@ -0,0 +1,117 @@
# 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) — returns to list view at the same page
- `🧪 Use` (success) — only shown if item type is CONSUMABLE with effects defined
- `🗑 Discard` (danger) — drops one unit of the item
**Container:** `ContainerBuilder` with accent color matching the item's rarity color.
### 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