Add comprehensive design document for project aliases and APK environment classification feature. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
193 lines
5.8 KiB
Markdown
193 lines
5.8 KiB
Markdown
# 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
|