docs: add design document for enhanced download UI

Add comprehensive design document for project aliases and
APK environment classification feature.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-04 20:16:53 +08:00
parent 3fde67c86f
commit 6182c38366

View File

@@ -0,0 +1,192 @@
# Design: Enhanced Download UI Readability
**Date:** 2026-03-04
**Status:** Approved
## Overview
Enhance the download UI to improve readability by:
1. Displaying project aliases instead of directory names
2. Classifying APKs by environment (dev, sandbox, product, other)
3. Displaying APKs in a list format with commit ID, file size, and creation time
4. Organizing APKs into tabs by environment
## Requirements
### Project Aliases
- Store project display name mappings in `lib/project-aliases.json`
- Example: `FT``外放构建`, `FT_DEV``开发构建`
- Fallback: Display directory name if no alias exists
### APK Classification
- Categories: `dev`, `sandbox`, `product`, `other`
- APK filename format: `ft_[environment]_[version]_[commit_id].apk`
- Environment suffix is optional; if missing, default to `product`
- Unmatched formats go to `other` tab
### Display Format
- List view with columns: Commit ID, File Size, Creation Time, Download
- Always display all 4 tabs (dev, product, sandbox, other)
- Commit filter applies globally to all tabs
## Architecture
### New Files
- `lib/project-aliases.json` - Project name alias configuration
- `lib/apk-parser.ts` - APK filename parsing utilities
### Modified Files
- `lib/utils.ts` - Add alias lookup utility
- `app/api/projects/route.ts` - Return projects with aliases
- `app/api/apks/route.ts` - Add environment metadata to response
- `app/download/SearchForm.tsx` - Use project aliases in dropdown
- `app/download/ApkList.tsx` - Replace card grid with tabbed list view
### Data Flow
1. **Project Selection:**
- Client loads projects → API reads `project-aliases.json` → returns with display names
- `SearchForm` displays `displayName`, submits `name` to API
2. **APK Fetching:**
- User selects project/version → client fetches APKs
- API uses `parseApkFilename()` to extract environment
- Returns enriched APKs with `environment` field
3. **Display:**
- Client receives APKs with `environment` field
- Organizes into tabs (dev, product, sandbox, other)
- Displays in list format
## Data Structures
### APK Parser Types
```typescript
export type ApkEnvironment = 'dev' | 'sandbox' | 'product' | 'other';
export interface ParsedApkMetadata {
environment: ApkEnvironment;
commit: string | null;
isValid: boolean;
}
```
### Project Alias Config
```json
{
"FT": "外放构建",
"FT_DEV": "开发构建"
}
```
### Enriched APK Response
```typescript
{
name: string;
environment: ApkEnvironment;
commit: string | null;
size: number;
modifiedAt: string; // ISO string
downloadUrl: string;
}
```
## Components
### ApkList (Restructured)
- Replaces grid of `ApkCard` with tabbed list view
- Manages tab state (active tab, filtered APKs)
- Handles commit filter globally
- Delegates list display to `ApkTable` component
### ApkTable (New)
- Table with columns: Commit ID, File Size, Creation Time, Download
- Styled with hover effects
- Empty state handling per tab
### SearchForm (Updated)
- Uses `getProjectDisplayName()` for project dropdown
- Maintains existing functionality
## Error Handling
### Project Alias Edge Cases
- Missing alias file: Fallback to directory names
- Project not in aliases: Display directory name
- Malformed JSON: Log error, fallback to directory names
### APK Filename Parsing Edge Cases
- No environment in filename: Default to `product`
- No commit ID: `commit` field is `null`
- Unmatched format: `environment: "other"`, still display
- Invalid characters: Handle gracefully
### UI Edge Cases
- Empty tab: Display "暂无 APK 文件"
- All tabs empty: Show helpful message
- Loading state: Skeleton or spinner
- API error: Display error message, allow retry
## APK Filename Parsing Logic
### Regex Pattern
```typescript
const APK_PATTERN = /^ft_([a-z]+)?_([0-9_]+)_([a-f0-9]{7,})\.apk$/;
// ^^^ ^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^
// ft env(opt) version commit id
```
### Parsing Rules
1. Try to match standard pattern: `ft_[env]_[version]_[commit].apk`
2. Extract environment if present, otherwise default to `product`
3. If pattern doesn't match, assign `environment: "other"`
4. Extract commit ID from filename
5. Use file system metadata for size and modified time
## Testing Strategy
### Unit Tests
- `lib/__tests__/apk-parser.test.ts`: Test filename parsing for all environments
- `lib/__tests__/project-aliases.test.ts`: Test alias lookup logic
### Integration Tests
- API tests for `/api/projects` with alias data
- API tests for `/api/apks` returning enriched metadata
### Component Tests
- `ApkTable`: Renders correct columns, handles empty states
- `ApkList`: Tabs work correctly, filters apply globally
- `SearchForm`: Displays project aliases correctly
### Manual Testing Checklist
- [ ] Projects display with aliases (FT → 外放构建)
- [ ] Projects without aliases show directory names
- [ ] APKs grouped correctly in 4 tabs
- [ ] Product APKs (no env suffix) go to "product" tab
- [ ] Dev/sandbox APKs go to correct tabs
- [ ] Invalid format APKs go to "other" tab
- [ ] Commit filter applies to all tabs
- [ ] List shows Commit ID, File Size, Creation Time
- [ ] Download button works correctly
## Implementation Approach
**Selected:** Approach 2 - Shared Utility Functions + Client-side Organization
**Why:**
- Clean separation of concerns
- Reusable parsing utilities
- Type-safe with TypeScript
- Easier to unit test parsing logic
- Client receives all APKs once, then handles tab organization
- Performance overhead is minimal for this use case
## Success Criteria
1. Project aliases display correctly in dropdown
2. APKs are correctly classified by environment
3. All 4 tabs display correctly (dev, product, sandbox, other)
4. List view shows correct columns (Commit ID, File Size, Creation Time)
5. Commit filter works globally across all tabs
6. Edge cases handled gracefully (missing aliases, invalid formats)
7. All tests pass