Files
aurorabot/docs/superpowers/specs/2026-03-28-inventory-display-redesign.md
syntaxbullet 289044e26f docs: add inventory display redesign spec
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 16:56:45 +01:00

118 lines
6.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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