CardApp Feature Map
Complete catalog of all implemented features as of 2026-04-11.
Authentication & Accounts
| Endpoint | Method | Description |
|---|---|---|
/api/auth/login/email | POST | Send magic link email |
/api/auth/verify | GET | Magic link callback (sets cookie, redirects) |
/api/auth/login/{provider} | GET | Initiate OAuth redirect (Google, Microsoft, Discord) |
/api/auth/callback/{provider} | GET | OAuth callback |
/api/auth/passkey/login-options | POST | Get WebAuthn assertion challenge |
/api/auth/passkey/login | POST | Validate assertion and sign in |
/api/auth/passkey/register-options | POST | Get WebAuthn registration challenge (auth required) |
/api/auth/passkey/register | POST | Complete passkey registration (auth required) |
/api/auth/me | GET | Get current user info |
/api/auth/logout | POST | Sign out |
/api/account | GET | Get account details |
/api/account | PUT | Update display name |
/api/account/upgrade | POST | Upgrade to Vendor account |
/api/account/logins | GET | List linked OAuth providers |
/api/account/link/{provider} | GET | Initiate provider linking |
/api/account/link/callback | GET | Provider linking callback |
/api/account/link/{provider} | DELETE | Unlink provider |
/api/account/passkeys | GET | List registered passkeys |
/api/account/passkeys/{id} | DELETE | Remove a passkey |
Frontend pages: /login, /account
Details:
- Three sign-in methods: passkeys (WebAuthn/FIDO2), OAuth (Google, Microsoft, Discord), magic link email
- ASP.NET Identity with cookie-based authentication (30-day sliding window)
- No client-side tokens — browser sends cookie automatically via
credentials: "include" - Account linking: connect multiple OAuth providers to a single account
- Passkey management: register, list, and remove passkeys from account settings
- Roles: User, Vendor, Admin, Owner, Banned
- Owner auto-promotion via
Auth:OwnerEmailconfig - See authentication.md for full details
Card Catalog & Search
| Endpoint | Method | Description |
|---|---|---|
/api/cards | GET | Search cards with filters, sorting, pagination |
/api/cards/{id} | GET | Single card detail with prices and collection qty |
/api/sets | GET | List all card sets |
/api/metadata | GET | Available filter values (types, rarities, etc.) |
Frontend pages: / (search), /cards/[id] (detail)
Details:
- ~15,000+ Pokemon TCG cards loaded in-memory from JSON submodule
- Full-text search across card name, artist, flavor text, abilities, and attacks
- Filter by: set, type, rarity, artist, supertype, subtypes
- Sort by: name, number, rarity, HP, release date, price
- Infinite scroll pagination
- Cards enriched with collection quantity badges and market prices
Inventory Management
| Endpoint | Method | Description |
|---|---|---|
/api/inventory | GET | List all user's inventory items |
/api/inventory | POST | Add card (auto-increments if duplicate) |
/api/inventory/{id} | PUT | Update quantity and/or purchase price |
/api/inventory/{id} | DELETE | Remove from collection |
/api/inventory/value | GET | Portfolio valuation with per-item gain/loss |
/api/inventory/batch | POST | Batch operations (vendor only) |
Frontend page: /inventory
Details:
- Per-user collections with quantity tracking
- Purchase price tracking with gain/loss calculation
- Market value aggregation across all owned cards
- Quick-add from search results
Pricing & Price History
| Endpoint | Method | Description |
|---|---|---|
/api/prices/{cardId} | GET | Price history (configurable days, source, variants) |
Details:
- Price history charts with configurable date range (Recharts line chart)
- Multiple variants: holofoil, reverse holofoil, normal, 1st edition
- Price types: low, mid, high, market, direct low
- 7 price sources: Scrydex (primary), TCGdex, PokeTrace, PokemonPriceTracker, PriceCharting, TAG, PokemonAPI
- Scheduled price fetching via K8s CronJob (daily at 6 AM UTC)
Price Alerts
| Endpoint | Method | Description |
|---|---|---|
/api/alerts | GET | List recent price alerts |
/api/alerts/unread-count | GET | Unread alert count |
/api/alerts/{id}/read | POST | Mark alert as read |
/api/alerts/read-all | POST | Mark all alerts as read |
/api/alerts/threshold | GET | Get alert threshold percentage |
/api/alerts/threshold | PUT | Set alert threshold |
Frontend component: AlertBell in navbar
Details:
- Triggered when market price changes exceed user-configured threshold
- Unread count badge with 60-second polling
- Dropdown with alert history, card navigation, threshold configuration
Vendor: Inventory Tags
| Endpoint | Method | Description |
|---|---|---|
/api/inventory/tags | GET | List user's tags |
/api/inventory/tags | POST | Create tag with name and color |
/api/inventory/tags/{id} | PUT | Update tag |
/api/inventory/tags/{id} | DELETE | Delete tag (cascades to assignments) |
/api/inventory/{itemId}/tags | POST | Assign tags to item |
/api/inventory/{itemId}/tags/{tagId} | DELETE | Remove tag from item |
Frontend components: TagManager, TagBadge
Details:
- Color-coded tags for organizing inventory
- Filter inventory by tag
- Tags auto-created during CSV import
- Unique per user (name + userId constraint)
Vendor: Custom Pricing
| Endpoint | Method | Description |
|---|---|---|
/api/inventory/{itemId}/price | GET | Get custom price |
/api/inventory/{itemId}/price | PUT | Set custom price (strategy + value) |
/api/inventory/{itemId}/price | DELETE | Remove custom price |
Frontend component: PriceEditor
Details:
- Three strategies: Fixed, MarkupPercent, MarkupFlat
- MarkupPercent:
market * (1 + markup/100) - MarkupFlat:
market + markup - Markup prices auto-recalculate when market prices update
Vendor: Bulk Import/Export
| Endpoint | Method | Description |
|---|---|---|
/api/inventory/import | POST | CSV file upload |
/api/inventory/export | GET | CSV download |
Frontend page: /vendor/import
Details:
- CSV columns:
CardId, Quantity, PurchasePrice, Tags - Validates CardId against in-memory card catalog
- Upserts: increments quantity for existing cards
- Tags column: semicolon-separated, auto-creates missing tags
- Returns detailed report: added/updated/failed counts with per-row failure reasons
Vendor: Batch Operations
| Operation | Description |
|---|---|
updateQuantity | Set quantity on all selected items |
addTags | Assign tags to all selected items |
removeTags | Remove tags from all selected items |
delete | Delete all selected items (cascades prices + tags) |
Frontend component: BatchToolbar on inventory page
Details:
- Multi-select checkbox UI on inventory table
- Vendor-only access
- Ownership validation (only operates on user's own items)
Vendor: Analytics Dashboard
| Endpoint | Method | Description |
|---|---|---|
/api/vendor/analytics | GET | Dashboard data (value history, top cards, trade stats) |
Frontend page: /vendor/dashboard
Details:
- Collection value over time (Recharts LineChart, configurable days)
- Top 10 most valuable cards with images and market prices
- Trade stats placeholder (wired for Phase 3)
Vendor: Public Storefront
| Endpoint | Method | Description |
|---|---|---|
/api/vendors/{displayName} | GET | Public storefront data |
Frontend page: /vendors/[displayName]
Details:
- Public (no auth required)
- Shows vendor's inventory with custom prices and tags
- Search by card name, filter by tags
- Pagination support
- Displays shop name and description from VendorProfile
Trading System
| Endpoint | Method | Description |
|---|---|---|
/api/trades | GET | List user's trades (filterable by status) |
/api/trades/{id} | GET | Trade detail with items and history |
/api/trades | POST | Propose a new trade |
/api/trades/{id}/accept | POST | Accept trade (atomic inventory transfer) |
/api/trades/{id}/decline | POST | Decline trade |
/api/trades/{id}/counter | POST | Counter with modified items |
/api/trades/{id}/cancel | POST | Cancel trade |
/api/notifications | GET | List trade notifications |
/api/notifications/unread-count | GET | Unread notification count |
/api/notifications/{id}/read | POST | Mark notification as read |
/api/notifications/read-all | POST | Mark all notifications as read |
SignalR Hubs:
/hubs/trade— Per-trade groups, events: TradeUpdated, TradeItemsChanged, TradeCompleted/hubs/notifications— Per-user groups, events: NewNotification, UnreadCountChanged
Frontend pages: /trades, /trades/[id], /trades/propose
Details:
- Request-based trading: propose, counter, accept/decline/cancel
- Real-time updates via SignalR when both parties are online
- Atomic inventory transfer on accept (database transaction)
- Trade history with JSON snapshots at each state change
- Counter flow: modify card selections, roles flip
- Conflict detection: validates quantities at accept time
- Pokemon-inspired trade completion animation (4-phase CSS keyframes)
- Trade notification bell with real-time unread count
- All party combinations: user-to-user, vendor-to-user, vendor-to-vendor
Trade Animation
4-phase sequence (~3.5s total), Catppuccin Macchiato themed:
- Reveal (0.8s) — Cards fade in on opposite sides with glow
- Approach (1.0s) — Cards slide to center with rotation and energy trails
- Flash & Swap (0.7s) — Yellow radial glow, cards cross over
- Settle (1.0s) — Swapped positions, "Trade Complete!" text, shimmer particles
- Pure CSS @keyframes, no animation libraries
- Actual card images from pokemon-tcg-data
- Multi-card: stacked with offset, top card leads
prefers-reduced-motion: skips to end state- Dismissible via click or Escape
- Rendered as React portal overlay
- Triggered by TradeCompleted SignalR event
Wishlist
| Endpoint | Method | Description |
|---|---|---|
/api/wishlist | GET | List wishlist items |
/api/wishlist | POST | Add card to wishlist |
/api/wishlist/{id} | DELETE | Remove from wishlist |
Frontend page: /wishlist
Details:
- Per-user wishlist for tracking cards you want
- Quick-add from card detail page
- Cards display with images, names, and market prices
Artists
| Endpoint | Method | Description |
|---|---|---|
/api/artists | GET | List all artists with aggregate stats |
Frontend page: /artists
Details:
- Browse all card artists
- Aggregate stats per artist (card count, collection count, total value)
- Click artist to filter card search by that artist
Grading
| Endpoint | Method | Description |
|---|---|---|
/api/grading/{cardId} | GET | Population data from grading services |
Frontend page: /cards/[id]/grading
Details:
- Population data from grading services (PSA, CGC, TAG, ACE)
- EV calculator for expected grading value analysis
- Population chart visualization
Admin Features
| Endpoint | Method | Description |
|---|---|---|
/api/admin/users | GET | List users with roles |
/api/admin/users/{id}/role | PUT | Assign role to user |
/api/admin/expenses | GET/POST | Expense ledger management |
/api/admin/rarity-ranks | GET/PUT | Custom rarity ordering |
/api/admin/margin-rules | GET/POST/PUT/DELETE | Vendor margin rules |
Frontend pages: /admin, /admin/users, /admin/expenses, /admin/price-sync, /admin/rarity-ranks
Details:
- User management with role assignment (User, Vendor, Admin, Owner, Banned)
- Price sync control panel for on-demand price fetching
- Expense ledger for tracking operational costs
- Rarity rank editor for custom rarity ordering
Exchange Rates
Details:
- Background service periodically refreshes currency exchange rates
- Multi-currency display via CurrencyContext on the frontend
- Cached exchange rates stored in database
Custom Items
| Endpoint | Method | Description |
|---|---|---|
/api/custom-items | GET/POST | User-created non-card inventory items |
/api/custom-items/{id} | PUT/DELETE | Manage custom items |
Details:
- Users can add non-card items to their inventory (e.g., accessories, sealed products)
- Custom pricing support
Database Schema
| Table | Description |
|---|---|
| AspNetUsers | User accounts (extends IdentityUser with DisplayName, CreatedAt, UpdatedAt) |
| AspNetRoles | Roles (User, Vendor, Admin, Owner, Banned) |
| AspNetUserRoles | User-role assignments |
| AspNetUserLogins | Linked OAuth providers (LoginProvider, ProviderKey) |
| AspNetUserClaims/RoleClaims/UserTokens | Identity infrastructure |
| StoredPasskeys | WebAuthn credentials (CredentialId, PublicKey, SignCount, FriendlyName) |
| VendorProfiles | Shop name, description, public flag |
| InventoryItems | Card ownership per user (quantity, purchase price, condition) |
| CustomItems | Non-card inventory items |
| InventoryTags | User-created color-coded tags |
| InventoryItemTags | Tag-to-item assignments (join table) |
| CustomPrices | Vendor pricing (strategy: Fixed, MarkupPercent, MarkupFlat) |
| MarginRules | Automated vendor pricing rules |
| PriceSnapshots | Daily market prices per card/variant/source |
| SalesSnapshots | Sales data per card/variant/source |
| PriceAlerts | Price change notifications |
| GradePopulations | Grading stats from PSA, CGC, TAG, ACE |
| WishlistItems | Cards users want to acquire |
| Trades | Trade proposals between users (status, timestamps) |
| TradeItems | Cards offered by each side in a trade |
| TradeHistory | State change log with JSON snapshots |
| Notifications | Trade event notifications per user |
| ExpenseLedgerEntries | Operational cost tracking |
| ExchangeRates | Cached currency exchange rates |
| RarityRanks | Custom rarity ordering |
| DocumentationSnapshots | Price source documentation cache |
| AppSettings | Key-value config (alert thresholds) |
Infrastructure
| Component | Technology |
|---|---|
| Frontend | Next.js 16, React 19, Tailwind CSS 4, shadcn/ui, Recharts, @microsoft/signalr |
| Real-time | ASP.NET Core SignalR (WebSocket with fallback) |
| Auth | ASP.NET Identity, FIDO2 (passkeys), OAuth 2.0 (Google, Microsoft, Discord) |
| Backend | ASP.NET Core 10, EF Core 10 |
| Database | PostgreSQL 15 |
| Card Data | pokemon-tcg-data git submodule (~15,000+ cards) |
| Pricing | 7 price sources (Scrydex primary), daily CronJob at 6 AM UTC |
| Postfix (SMTP, magic link delivery) | |
| Deployment | Kubernetes, Terraform, Traefik ingress with TLS |
| Auto-deploy | Keel (polls GHCR every 2 min) |
| CI/CD | GitHub Actions → GitHub Container Registry (GHCR) |
Frontend Page Map
| Route | Auth | Description |
|---|---|---|
/ | No | Card search with filters and infinite scroll |
/cards/[id] | No | Card detail with prices, grading link, add-to-inventory |
/cards/[id]/grading | No | Grading population data and EV calculator |
/login | No | Sign-in page (passkey, OAuth, magic link) |
/account | Yes | Profile, connected accounts, passkey management |
/inventory | Yes | Personal collection with value tracking |
/wishlist | Yes | Wishlist items |
/artists | No | Browse artists with aggregate stats |
/trades | Yes | Trade list with status filter tabs |
/trades/[id] | Yes | Trade detail with actions and real-time updates |
/trades/propose | Yes | Propose trade with card selection |
/vendor/dashboard | Vendor | Analytics with charts |
/vendor/import | Vendor | Bulk CSV import/export |
/vendors/[displayName] | No | Public vendor storefront |
/admin | Admin | Admin dashboard |
/admin/users | Admin | User management with role assignment |
/admin/expenses | Admin | Expense ledger |
/admin/price-sync | Admin | Price fetcher control panel |
/admin/rarity-ranks | Admin | Rarity rank editor |