# AI Gateway Frontend

AI 网关管理前端，提供供应商配置和用量统计界面。

## 技术栈

- **运行时**: Bun
- **构建工具**: Vite
- **语言**: TypeScript (strict mode)
- **框架**: React
- **UI 组件库**: TDesign
- **路由**: React Router v7
- **数据获取**: TanStack Query v5
- **样式**: SCSS Modules（禁止使用纯 CSS）
- **测试**: Vitest + React Testing Library + Playwright
- **代码格式化**: Prettier

## API 层

### 字段转换机制

后端使用 `snake_case`，前端使用 `camelCase`，API 层自动转换：

```typescript
// 发送请求时：camelCase → snake_case
toApi({ providerId: 'openai' }) // → { provider_id: "openai" }

// 接收响应时：snake_case → camelCase
fromApi({ provider_id: 'openai' }) // → { providerId: "openai" }
```

### 统一请求函数

```typescript
export async function request<T>(method: string, path: string, body?: unknown): Promise<T>
```

- 自动处理字段转换
- 自动处理 204 响应（无 body）
- 抛出 `ApiError` 包含 `status`、`code`、`message`

### 错误处理

```typescript
class ApiError extends Error {
  status: number // HTTP 状态码
  code?: string // 业务错误码
  message: string // 错误消息
}
```

## TanStack Query 模式

### Query Keys 定义

```typescript
// src/hooks/useProviders.ts
export const providerKeys = {
  all: ['providers'] as const,
}

// src/hooks/useModels.ts
export const modelKeys = {
  all: ['models'] as const,
  byProvider: (providerId: string) => [...modelKeys.all, { providerId }] as const,
}
```

### Mutation 使用

```typescript
const mutation = useMutation({
  mutationFn: createProvider,
  onSuccess: () => {
    queryClient.invalidateQueries({ queryKey: providerKeys.all })
  },
})
```

## 项目结构

```
frontend/
├── src/
│   ├── api/                    # API 层
│   │   ├── client.ts           # 统一 request<T>() + 字段转换
│   │   ├── providers.ts        # Provider CRUD
│   │   ├── models.ts           # Model CRUD
│   │   └── stats.ts            # Stats 查询
│   ├── components/
│   │   └── AppLayout/          # 侧边栏导航布局
│   ├── hooks/                  # TanStack Query hooks
│   │   ├── useProviders.ts
│   │   ├── useModels.ts
│   │   └── useStats.ts
│   ├── pages/
│   │   ├── Providers/          # 供应商管理（含内嵌模型管理）
│   │   ├── Stats/              # 用量统计
│   │   ├── Settings/           # 设置（开发中）
│   │   └── NotFound.tsx
│   ├── routes/
│   │   └── index.tsx           # 路由配置
│   ├── types/
│   │   └── index.ts            # 类型定义
│   ├── __tests__/              # 测试
│   │   ├── setup.ts
│   │   ├── api/
│   │   ├── hooks/
│   │   └── components/
│   ├── App.tsx
│   ├── main.tsx
│   └── index.scss
├── e2e/                        # Playwright E2E 测试
├── vitest.config.ts
├── playwright.config.ts
├── tsconfig.json
├── vite.config.ts
└── package.json
```

## 运行方式

### 安装依赖

```bash
bun install
```

### 开发模式

```bash
bun run dev
```

前端将在端口 5173 启动，API 请求通过 Vite proxy 转发到后端（localhost:9826）。

### 构建生产版本

```bash
bun run build
```

### 代码检查

```bash
bun run lint          # ESLint 检查
bun run format:check  # Prettier 格式检查
bun run check         # 同时检查 lint 和格式
```

### 代码格式化

```bash
bun run format        # 格式化所有文件
bun run fix           # 修复 lint 问题并格式化
```

VS Code 保存时自动格式化（需安装 Prettier 扩展）。

## 测试

### 单元测试 + 组件测试

```bash
bun run test            # 运行所有测试
bun run test:watch      # 监听模式
bun run test:coverage   # 生成覆盖率报告
```

### E2E 测试

```bash
bun run test:e2e
```

## 功能

### 供应商管理

- 查看供应商列表（TDesign Table）
- 添加新供应商（Modal Form）
- 编辑供应商配置
- 删除供应商（Popconfirm 确认）
- API Key 脱敏显示
- 启用/禁用状态标签
- **协议字段**：支持 OpenAI 和 Anthropic 协议选择

### 模型管理

- 展开供应商行查看关联模型
- 添加/编辑/删除模型
- 按供应商筛选模型
- **统一模型 ID**：显示格式为 `provider_id/model_name`，用于跨协议模型识别
- **UUID 自动生成**：创建模型时后端自动生成 UUID，无需手动输入 ID

### 用量统计

- 查看统计数据
- 按供应商筛选
- 按模型筛选
- 按日期范围筛选（DatePicker.RangePicker）

## 测试策略

### 目录结构

```
__tests__/
├── setup.ts           # 测试配置（happy-dom）
├── api/               # API 层测试
│   └── client.test.ts
├── hooks/             # TanStack Query Hook 测试
│   ├── useProviders.test.ts
│   └── useModels.test.ts
└── components/        # 组件测试
    └── AppLayout.test.tsx
```

### E2E 测试

- 位于 `e2e/` 目录
- 使用 Playwright
- 自动启动后端服务（临时端口 19026）
- 配置文件：`playwright.config.ts`

### Mock 策略

- API 层测试使用 MSW（Mock Service Worker）
- Hook 测试使用 `@testing-library/react-hooks`
- 组件测试使用 `@testing-library/react`

## 环境变量

| 变量            | 开发环境 | 生产环境 | 说明                            |
| --------------- | -------- | -------- | ------------------------------- |
| `VITE_API_BASE` | （空）   | `/api`   | API 基础路径，空则走 Vite proxy |

**E2E 测试特有**：

- `NEX_BACKEND_PORT` - E2E 后端端口（默认 19026）
- `NEX_E2E_TEMP_DIR` - E2E 临时目录

## 开发规范

- 所有样式使用 SCSS，禁止使用纯 CSS 文件
- 组件级样式使用 SCSS Modules（\*.module.scss）
- 图标优先使用 TDesign 图标（tdesign-icons-react）
- TypeScript strict 模式，禁止 any 类型
- API 层自动处理 snake_case ↔ camelCase 字段转换
- 使用路径别名 `@/` 引用 src 目录
- 代码格式化使用 Prettier，配置见 `.prettierrc`
- 编辑器配置见 `.editorconfig`（统一缩进、换行符、编码）

### 环境要求

- Bun 1.0 或更高版本
- VS Code 推荐安装 Prettier 和 ESLint 扩展（见 `.vscode/extensions.json`）

### 添加新页面

1. 在 `src/pages/` 创建页面目录和组件
2. 在 `src/hooks/` 创建对应的 TanStack Query hook
3. 在 `src/routes/index.tsx` 添加路由
4. 在 `src/components/AppLayout/index.tsx` 的 Menu 中添加 MenuItem
