Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 129 additions & 0 deletions docs/plans/2026-01-24-mobile-leaderboard-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# Mobile Leaderboard Optimization Design

## Problem

The leaderboard page has usability issues on mobile devices:

1. **Tab labels truncated** - "供应商缓存命中率排行" gets cut off, appearing as garbled text
2. **Table too cramped** - 4+ columns squeezed into narrow viewport
3. **Filter inputs crowded** - Two TagInputs side by side have limited width

## Solution Overview

Transform the leaderboard from table-based layout to card-based layout on mobile, with simplified tab labels and stacked filter inputs.

## Design Details

### 1. Tab Label Simplification

Use shorter labels on mobile (< 768px):

| Desktop | Mobile |
|---------|--------|
| 用户排行 | 用户 |
| 供应商排行 | 供应商 |
| 供应商缓存命中率排行 | 缓存率 |
| 模型排行 | 模型 |

Implementation: Use `useIsMobile()` hook to conditionally render tab labels.

### 2. Card-Based Layout

Replace table with expandable cards on mobile.

#### Default View (Collapsed)

```
┌─────────────────────────────────────┐
│ 🏆 #1 username $18.01M │ → tap to expand
├─────────────────────────────────────┤
│ 🥈 #2 another_user $5.32M │
├─────────────────────────────────────┤
│ 🥉 #3 test_account $2.10M │
└─────────────────────────────────────┘
```

- Left: Rank badge (reuse existing Trophy/Medal/Award icons)
- Center: Name (user/provider/model depending on scope)
- Right: Primary metric (cost/tokens)
- Visual: Top 3 highlighted with `bg-muted/50`

#### Expanded View

```
┌─────────────────────────────────────┐
│ 🏆 #1 default ▲ 收起 │
│─────────────────────────────────────│
│ 请求数 Token数 消耗 │
│ 299 18.01M $12.50 │
└─────────────────────────────────────┘
```

Fields by scope:

| Scope | Expanded Fields |
|-------|-----------------|
| User | requests, tokens, cost |
| Provider | requests, cost, tokens, successRate, avgTtfbMs, avgTokensPerSecond |
| CacheHitRate | requests, cacheHitRate, cacheReadTokens, totalInputTokens |
| Model | requests, tokens, cost, successRate |

Layout:
- 3-4 fields: single row `grid-cols-3`
- 5-6 fields: two rows `grid-cols-3`

### 3. Filter Area

Mobile layout (stacked):

```
┌─────────────────────────────────────┐
│ [按用户标签筛选... ] │ ← full width
│ [按用户分组筛选... ] │ ← full width
├─────────────────────────────────────┤
│ [今日] [本周] [本月] [全部] │ ← keep horizontal
│ [<] [2026-01-24 ] [>] │
└─────────────────────────────────────┘
```

Changes:
- TagInputs stack vertically on mobile
- Each TagInput takes full width
- Date picker area remains unchanged

### 4. Responsive Switching

Use existing `useIsMobile()` hook from `src/lib/hooks/use-mobile.ts`:
- Breakpoint: 768px
- Mobile (< 768px): Render card components
- Desktop (>= 768px): Keep existing table

## Implementation Plan

### Files to Create

1. `src/app/[locale]/dashboard/leaderboard/_components/mobile-leaderboard-card.tsx`
- Reusable card component for all scopes
- Props: rank, data, scope, expanded, onToggle

### Files to Modify

1. `src/app/[locale]/dashboard/leaderboard/_components/leaderboard-view.tsx`
- Add `useIsMobile()` hook
- Conditionally render mobile tabs labels
- Stack TagInputs on mobile
- Render cards instead of table on mobile

2. `messages/*/dashboard/leaderboard.json` (all 5 languages)
- Add short tab labels: `tabs.userRankingShort`, `tabs.providerRankingShort`, etc.

### Files Unchanged

- `leaderboard-table.tsx` - Desktop table component, no changes needed
- `date-range-picker.tsx` - Already works on mobile

## Related Work

This follows the same pattern as the mobile logs optimization:
- `src/app/[locale]/dashboard/logs/_components/mobile-log-card.tsx`
- `src/app/[locale]/dashboard/logs/_components/mobile-logs-list.tsx`
122 changes: 122 additions & 0 deletions docs/plans/2026-01-24-mobile-usage-logs-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# Mobile Usage Logs Card Layout Design

## Overview

Optimize the usage logs display on mobile devices (< 768px) by replacing the table layout with a card-based layout that shows all essential information without truncation.

## Problem

Current table layout on mobile:
- 11 columns squeezed into narrow screen
- Content truncated with `...` everywhere
- Users cannot see complete information
- Poor mobile browsing experience

## Solution

Switch to card-based layout on mobile while keeping desktop table unchanged.

## Card Structure

```
+-------------------------------------+
| [Header] Time + Status Badge |
| Left: Relative time (3s ago) |
| Right: Status badge (OK 200) |
+-------------------------------------+
| [Identity] User + Provider + Model |
| Username - Provider name |
| Model name (with redirect arrow) |
+-------------------------------------+
| [Data] Tokens + Cache + Cost |
| Col1: Input/Output tokens |
| Col2: Cache write/read |
| Col3: Cost amount |
+-------------------------------------+
| [Performance] Duration + TTFB + Rate|
| Total time - TTFB - Output rate |
+-------------------------------------+
```
Comment on lines +21 to +39
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

为代码块补充语言标识以通过 markdownlint。
目前的围栏代码块缺少语言标识,建议统一标注为 text

修改建议
-```
+```text
+-------------------------------------+
...
-```
+```text
+-----------+-----------+---------+
...
-```
+```text
+src/app/[locale]/dashboard/logs/_components/
基于静态分析提示。

Also applies to: 66-72, 80-85

🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

21-21: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
In `@docs/plans/2026-01-24-mobile-usage-logs-design.md` around lines 21 - 39, The
fenced code blocks in the design doc are missing a language identifier; update
each markdown fence (``` ... ```) shown in the diff to include the "text"
language tag (i.e. change ``` to ```text) for the block that starts with the
ASCII layout diagram and the other two occurrences referenced (the blocks around
the other ASCII tables), so all three fenced code blocks are ```text to satisfy
markdownlint.


## Visual Design

### Card Base Style
- Border radius: `rounded-lg`
- Border: `border`
- Gap between cards: `gap-3` (12px)
- Padding: `p-3` (12px)
- Click feedback: slight press effect

### Status Badges

| Status | Style | Example |
|--------|-------|---------|
| Success (200) | Green background | `OK 200` |
| Client error (4xx) | Orange background | `! 429` |
| Server error (5xx) | Red background | `X 500` |
| Blocked | Orange outline | `Blocked` |

### Special States
- **Session resume**: Small tag after provider name
- **Model redirect**: Arrow display `gpt-4 -> claude-sonnet`
- **Cost multiplier**: Badge next to cost `x1.50`
- **Non-billing request**: Muted card background `bg-muted/60`

### Data Section Layout
```
+-----------+-----------+---------+
| Tokens | Cache | Cost |
| In: 1.2K | W: 500 | $0.0234 |
| Out: 856 | R: 2.1K | |
+-----------+-----------+---------+
```
- Three equal-width columns
- Numbers right-aligned
- Labels left-aligned

## Implementation

### File Structure
```
src/app/[locale]/dashboard/logs/_components/
├── virtualized-logs-table.tsx # Add responsive detection
├── mobile-log-card.tsx # New: Single card component
└── mobile-logs-list.tsx # New: Mobile card list with virtual scroll
```

### Changes

1. **virtualized-logs-table.tsx**
- Import `useIsMobile()` hook
- Render `MobileLogsList` when `isMobile`
- Keep existing table for desktop

2. **mobile-log-card.tsx**
- Accept single log data
- Render four sections (header/identity/data/performance)
- Click triggers `onCardClick` to open detail dialog

3. **mobile-logs-list.tsx**
- Reuse existing `useInfiniteQuery` logic
- Reuse existing `useVirtualizer` for virtual scrolling
- Adjust `ROW_HEIGHT` to card height (~140px)

### Reused Components
- Data fetching: `getUsageLogsBatch`
- Detail dialog: `ErrorDetailsDialog`
- Time format: `RelativeTime`
- Currency format: `formatCurrency`
- Token format: `formatTokenAmount`

## Interaction

- Tap card: Open detail dialog (reuse ErrorDetailsDialog)
- Virtual scrolling: Infinite scroll with auto-fetch
- Pull to refresh: Supported via existing refresh logic

## Responsive Breakpoint

- `< 768px`: Card layout (mobile)
- `>= 768px`: Table layout (desktop)

Detection via `useIsMobile()` hook from `@/lib/hooks/use-mobile.ts`
2 changes: 2 additions & 0 deletions drizzle/0057_jazzy_blink.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE "providers" ALTER COLUMN "provider_vendor_id" DROP NOT NULL;--> statement-breakpoint
ALTER TABLE "providers" ADD COLUMN "group_priorities" jsonb DEFAULT 'null'::jsonb;
Loading