# Story 008.005: 移植残疾人管理UI（disability → @d8d/allin-disability-management-ui）

## Status
Ready for Review

## Story
**As a** 开发者，
**I want** 将disability管理页面从allin_system-master/client移植为独立UI包@d8d/allin-disability-management-ui，完成技术栈转换并集成区域选择器组件和创建可复用的残疾人选择器组件，
**so that** 我们可以将Allin系统的残疾人管理UI模块集成到当前项目中，遵循现有的UI包结构和编码标准，正确集成区域选择器组件用于残疾人区域信息管理，并提供可复用的残疾人选择器组件供其他模块（如订单管理）使用。

## Acceptance Criteria
1. 创建`allin-packages/disability-management-ui`目录结构
2. 完成组件转换：残疾人列表管理组件
3. **区域包集成**：集成`@d8d/area-management-ui`的区域选择器组件用于残疾人区域信息管理
4. 完成API客户端转换：与disability-module集成
5. 完成状态管理转换：残疾人数据状态
6. 完成表单转换：基础残疾人信息表单（包含区域选择）
7. **残疾人选择器组件**：创建可复用的残疾人选择器组件（复杂对话框模式），支持多维度搜索、筛选和批量选择
8. 配置package.json：依赖`@d8d/allin-disability-module`和`@d8d/area-management-ui`
9. 编写组件测试：验证残疾人管理功能（包含区域选择验证和选择器组件测试）
10. 通过类型检查和基本测试验证

## Tasks / Subtasks
- [x] 任务1：创建残疾人管理UI包基础结构 (AC: 1)
  - [x] 创建目录结构：`allin-packages/disability-management-ui/`
  - [x] 创建package.json：配置包名、依赖、脚本
  - [x] 创建TypeScript配置：`tsconfig.json`
  - [x] 创建测试配置：`vitest.config.ts`
  - [x] 创建主入口文件：`src/index.ts`

- [x] 任务2：分析源系统文件并创建API客户端 (AC: 4)
  - [x] 分析残疾人模块RPC路由定义
  - [x] 查看残疾人模块集成测试
  - [x] 创建RPC客户端管理器：`src/api/disabilityClient.ts`
  - [x] 创建API类型定义：`src/api/types.ts`（使用RPC推断类型）

- [x] 任务3：创建残疾人管理主组件 (AC: 2, 3, 6)
  - [x] 创建主管理组件：`src/components/DisabilityManagement.tsx`
  - [x] 集成区域选择器：在表单中集成`AreaSelect`组件
  - **注**：遵循表单组件模式，使用条件渲染两个独立的Form组件

- [x] 任务6：编写集成测试 (AC: 8)
  - [x] 创建主测试文件：`tests/integration/disability.integration.test.tsx`
  - [x] 实现完整CRUD流程测试
  - [x] 实现区域选择器集成测试
  - [x] 实现表单验证测试
  - [x] 实现错误处理测试

- [x] 任务7：验证和测试 (AC: 9)
  - [x] 运行`pnpm typecheck`确保无类型错误
  - [x] 运行`pnpm test`确保所有集成测试通过（7个测试全部通过，100%通过率）
  - [x] 验证区域选择器组件集成正常工作
  - [x] 验证表单验证和错误处理功能
  - [x] 验证组件导出和类型定义正确

- [x] 任务8：创建残疾人选择器组件（新增任务）
  - [x] 分析源系统残疾人选择器实现：`allin_system-master/client/app/admin/dashboard/order/SelectPersonModal.tsx`
    - **查看要点**：对话框模式、8个搜索字段、表格展示、黑名单确认逻辑
    - **搜索字段**：姓名、性别、残疾证号、联系电话、省份、城市、残疾类型、残疾等级
    - **区域集成**：省份→城市三级联动，集成`@d8d/area-management-ui`区域选择器
  - [x] 创建残疾人选择器组件：`src/components/DisabledPersonSelector.tsx`
    - **组件模式**：复杂对话框模式（Dialog组件）
    - **功能**：支持单选/多选模式、搜索筛选、分页、黑名单确认
    - **Props接口**：
      ```typescript
      interface DisabledPersonSelectorProps {
        open: boolean;
        onOpenChange: (open: boolean) => void;
        onSelect: (person: DisabledPerson | DisabledPerson[]) => void;
        mode?: 'single' | 'multiple';
        selectedIds?: number[];
        disabledIds?: number[];
      }
      ```
    - **搜索区域**：8个搜索字段，支持重置功能
    - **表格区域**：Table组件展示残疾人列表，包含选择操作
    - **黑名单逻辑**：黑名单人员需要二次确认
  - [x] 扩展API客户端支持搜索功能
    - **搜索API**：扩展残疾人API客户端，支持搜索参数传递
    - **分页支持**：集成分页参数到搜索查询
    - **类型定义**：创建搜索相关的类型定义
  - [x] 创建组件集成测试：`tests/integration/disabled-person-selector.integration.test.tsx`
    - **测试场景**：对话框打开/关闭、搜索功能、选择功能、黑名单确认、错误处理
    - **测试ID**：为关键交互元素添加`data-testid`属性
    - **参考文件**：`packages/advertisement-type-management-ui/tests/integration/advertisement-type-selector.integration.test.tsx`
  - [x] 更新包导出配置
    - **组件导出**：在`src/index.ts`和`src/components/index.ts`中导出`DisabledPersonSelector`组件
    - **类型导出**：确保相关类型定义正确导出
    - **用途说明**：作为可复用组件供其他UI包使用（如订单管理UI需要选择残疾人）

### 任务调整说明
根据UI包开发规范和经验，对原任务进行了优化：
1. **移除任务4（自定义Hook）**：主组件直接使用`useQuery`和`useMutation`，无需额外Hook封装
2. **移除任务5（表单Schema）**：直接使用后端模块的Schema，避免重复定义
3. **优化组件结构**：将表格、表单、模态框功能集成到主组件中，简化结构
4. **新增任务8（残疾人选择器组件）**：基于故事008.007的需求分析，添加可复用的残疾人选择器组件任务，采用复杂对话框模式，支持多维度搜索和筛选，供订单管理UI等其他模块使用

## Dev Notes

### 从前一个故事学到的关键经验（故事008.004）：
1. **Schema设计一致性验证**：必须查看后端模块的集成测试和路由定义来确保Schema设计正确，不能仅凭前端使用场景假设。
2. **API路径一致性验证**：必须根据后端实际路由设计前端API调用，不能假设为标准CRUD模式。
3. **测试精度优化**：在测试中使用test ID比文本查找更精确可靠。
4. **类型推导优化**：遵循现有UI包的模式，使用正确的RPC类型推导语法。
5. **表单组件模式规范**：必须使用条件渲染两个独立的Form组件，避免在单个Form组件上动态切换props。

### 技术栈规范 [Source: docs/architecture/ui-package-standards.md]
- **包结构规范**：标准目录结构，包含components、api、hooks、types目录
- **RPC客户端实现规范**：必须使用ClientManager单例模式管理RPC客户端生命周期
- **组件开发规范**：使用@d8d/shared-ui-components组件库，React Query进行状态管理
- **类型定义规范**：使用RPC推断类型，避免重新定义类型
- **测试规范**：集成测试、组件测试、单元测试分层结构
- **表单组件模式规范**：必须使用条件渲染两个独立的Form组件，避免在单个Form组件上动态切换props

### 项目结构信息 [Source: docs/architecture/source-tree.md]
- **UI包位置**：`allin-packages/disability-management-ui/`（Allin系统专属包目录）
- **参考实现**：
  - `allin-packages/platform-management-ui/`（已完成的平台管理UI包）
  - `allin-packages/channel-management-ui/`（已完成的渠道管理UI包）
  - `allin-packages/company-management-ui/`（已完成的公司管理UI包）
  - `allin-packages/salary-management-ui/`（已完成的薪资管理UI包）
- **后端模块**：`allin-packages/disability-module/`（对应的后端模块）
- **区域管理UI包**：`packages/area-management-ui/`（需要集成的区域选择器组件）

### 源系统文件路径
- **需要移植的源文件**：`allin_system-master/client/app/admin/dashboard/disability/page.tsx`
- **需要对照参考的文件**：
  - `allin-packages/platform-management-ui/src/components/PlatformManagement.tsx`
  - `allin-packages/platform-management-ui/src/api/platformClient.ts`
  - `allin-packages/platform-management-ui/src/api/types.ts`
  - `allin-packages/platform-management-ui/tests/integration/platform.integration.test.tsx`
  - `allin-packages/company-management-ui/src/components/CompanyManagement.tsx`
  - `allin-packages/company-management-ui/src/api/companyClient.ts`
  - `allin-packages/company-management-ui/src/types/index.ts`
- **区域选择器组件文件**：
  - `packages/area-management-ui/src/components/AreaSelect.tsx`
  - `packages/area-management-ui/src/api/areaClient.ts`

### 残疾人模块RPC API调用信息（基于集成测试和路由定义）
- **RPC客户端调用方式**（来自`allin-packages/disability-module/tests/integration/disability.integration.test.ts`和`allin-packages/disability-module/src/routes/disabled-person-custom.routes.ts`）：
  - `disabilityClientManager.get().createDisabledPerson.$post({ json: personData })` - 创建残疾人记录
  - `disabilityClientManager.get().updateDisabledPerson.$post({ json: personData })` - 更新残疾人记录
  - `disabilityClientManager.get().deleteDisabledPerson.$post({ json: { id } })` - 删除残疾人记录
  - `disabilityClientManager.get().getDisabledPersonList.$get({ query: { page, pageSize, filters, sortBy, sortOrder } })` - 获取残疾人列表（分页）
  - `disabilityClientManager.get().getDisabledPersonById.$get({ query: { id } })` - 获取残疾人详情

- **API路径映射**（来自`allin-packages/disability-module/src/routes/disabled-person-custom.routes.ts`）：
  - `POST /createDisabledPerson` → `disabilityClientManager.get().createDisabledPerson.$post`
  - `POST /updateDisabledPerson` → `disabilityClientManager.get().updateDisabledPerson.$post`
  - `POST /deleteDisabledPerson` → `disabilityClientManager.get().deleteDisabledPerson.$post`
  - `GET /getDisabledPersonList` → `disabilityClientManager.get().getDisabledPersonList.$get`
  - `GET /getDisabledPersonById` → `disabilityClientManager.get().getDisabledPersonById.$get`

- **残疾人数据字段**（来自`allin-packages/disability-module/src/schemas/disabled-person.schema.ts`）：
  - `name: string` - 姓名
  - `gender: string` - 性别
  - `idCard: string` - 身份证号
  - `disabilityId: string` - 残疾证号
  - `disabilityType: string` - 残疾类型
  - `disabilityLevel: string` - 残疾等级
  - `idAddress: string` - 身份证地址
  - `phone: string` - 联系电话
  - `province: string` - 省份
  - `city: string` - 城市
  - **注意**：源系统使用字符串省份城市，需要转换为区域ID格式

### 区域选择器集成方案
- **组件使用**：导入`AreaSelect`组件从`@d8d/area-management-ui`
- **数据转换**：将源系统的字符串省份城市转换为区域ID
  - 表单提交时：区域ID → 后端API
  - 数据显示时：区域ID → 区域名称显示
- **三级联动**：使用`AreaSelect`组件内置的省份→城市→区县三级联动功能
- **表单集成**：在残疾人表单中集成区域选择器，替换原有的省份、城市选择逻辑
- **数据格式**：`AreaSelect`组件返回`{ provinceId?, cityId?, districtId? }`格式
- **验证集成**：残疾人Schema中的区域字段验证与`AreaSelect`组件集成

### 残疾人选择器组件设计（新增）
- **组件模式**：复杂对话框模式（Dialog组件），参考源系统`SelectPersonModal.tsx`
- **搜索功能**：8个搜索字段，支持多维度筛选
  - 姓名（模糊搜索）
  - 性别（下拉选择：男/女）
  - 残疾证号（精确搜索）
  - 联系电话（模糊搜索）
  - 省份/城市（集成`AreaSelect`组件，三级联动）
  - 残疾类型（下拉选择，从字典获取）
  - 残疾等级（下拉选择，从字典获取）
- **表格展示**：DataTable展示残疾人列表，包含选择操作
- **选择模式**：支持单选和多选模式
- **特殊逻辑**：
  - 黑名单人员二次确认
  - 已选择/禁用人员标识
  - 分页支持
- **API集成**：扩展残疾人API客户端，支持搜索参数传递
- **复用性**：作为可复用组件导出，供其他UI包（如订单管理UI）使用

### 表单验证要求
- **必填字段**：姓名、性别、身份证号、残疾证号、残疾类型、残疾等级、联系电话
- **身份证号验证**：格式验证、唯一性验证（后端验证）
- **联系电话验证**：手机号格式验证
- **区域验证**：省份和城市必须选择

### Testing
**测试标准**：
- **测试框架**：Vitest + Testing Library
- **测试位置**：
  - 集成测试：`tests/integration/disability.integration.test.tsx`
- **测试覆盖要求**：
  - 完整CRUD流程测试
  - 区域选择器集成测试
  - **残疾人选择器组件测试**：对话框功能、搜索筛选、选择逻辑、黑名单确认
  - 表单验证测试（必填字段、身份证号格式、联系电话格式）
  - 错误处理测试（API错误、验证错误）
- **测试模式**：参考现有UI包的集成测试模式

**测试策略**：
- 使用`data-testid`进行元素定位，避免文本查找
- 模拟完整的用户交互流程
- 验证区域选择器的集成和数据转换
- **测试残疾人选择器组件**：对话框交互、搜索功能、选择逻辑、黑名单处理
- 测试表单验证和错误提示
- 覆盖各种错误场景

## Change Log
| Date | Version | Description | Author |
|------|---------|-------------|--------|
| 2025-12-04 | 1.3 | 添加残疾人选择器组件任务，支持故事008.007的订单管理需求 | John (PM) |
| 2025-12-03 | 1.2 | 修复测试问题和Schema错误消息，所有测试通过 | James (Dev Agent) |
| 2025-12-03 | 1.1 | 完成残疾人管理UI包开发，包含区域选择器集成 | James (Dev Agent) |
| 2025-12-03 | 1.0 | 初始创建故事008.005 | Scrum Master Bob |

## Dev Agent Record
*This section is populated by the development agent during implementation*

### Agent Model Used
- Claude Code (d8d-model) via Happy Engineering

### Debug Log References
- 类型检查发现多个类型错误，已修复：
  1. API客户端导入错误：使用`disabledPersonRoutes`而不是`disabledPersonCustomRoutes`
  2. 表单类型错误：使用RPC推断类型而不是Schema类型
  3. API响应类型处理：正确处理200状态码响应
  4. DataTablePagination组件props不匹配：使用`totalCount`而不是`total`

### Completion Notes List
1. **遵循UI包开发规范**：使用RPC推断类型，避免重复定义Schema
2. **区域选择器集成**：成功集成`@d8d/area-management-ui`的AreaSelect组件
3. **表单组件模式**：使用条件渲染两个独立的Form组件（创建表单和更新表单）
4. **测试策略优化**：为关键交互元素添加`data-testid`属性
5. **类型安全**：正确使用Hono RPC类型推导，确保类型安全

### 开发经验总结
1. **RPC类型推导最佳实践**：必须使用`InferResponseType`和`InferRequestType`进行类型推导
2. **Schema使用规范**：Schema仅用于表单验证，类型定义使用RPC推断
3. **API错误处理**：需要检查响应状态码，正确处理错误响应
4. **组件复用**：参考现有UI包（平台管理、公司管理）的实现模式
5. **测试Mock结构一致性**：测试中的mock结构必须与实际客户端管理器结构一致，参考其他UI包的测试实现
6. **测试选择器优化**：使用`data-testid`比文本查找更可靠，避免因重复文本导致的测试失败
7. **类型检查修复**：需要修复来自依赖包的类型错误，确保整体类型安全
8. **表单调试最佳实践**：在`form.handleSubmit`的第二个参数中添加`console.debug`调试信息，便于排查表单验证问题
9. **Schema错误消息本地化**：在Schema定义中添加中文错误消息`.message('xxx不能为空')`，提供更好的用户体验
10. **测试与实现一致性**：测试期望的错误消息必须与Schema定义的实际错误消息保持一致
11. **测试API调用逻辑验证**：测试中需要验证组件实际的API调用逻辑，确保mock数据与组件使用场景匹配
12. **lucide-react依赖处理**：测试中不需要mock lucide-react，直接使用实际组件即可

### File List
**新建文件：**
- `allin-packages/disability-management-ui/package.json` - 包配置
- `allin-packages/disability-management-ui/tsconfig.json` - TypeScript配置
- `allin-packages/disability-management-ui/vitest.config.ts` - 测试配置
- `allin-packages/disability-management-ui/src/index.ts` - 包入口
- `allin-packages/disability-management-ui/src/api/disabilityClient.ts` - API客户端
- `allin-packages/disability-management-ui/src/api/types.ts` - 类型定义
- `allin-packages/disability-management-ui/src/api/index.ts` - API导出
- `allin-packages/disability-management-ui/src/components/DisabilityManagement.tsx` - 主组件
- `allin-packages/disability-management-ui/src/components/DisabledPersonSelector.tsx` - 残疾人选择器组件
- `allin-packages/disability-management-ui/src/components/index.ts` - 组件导出
- `allin-packages/disability-management-ui/tests/setup.ts` - 测试配置
- `allin-packages/disability-management-ui/tests/integration/disability.integration.test.tsx` - 集成测试
- `allin-packages/disability-management-ui/tests/integration/disabled-person-selector.integration.test.tsx` - 残疾人选择器集成测试

**修改文件：**
- `allin-packages/disability-module/src/schemas/disabled-person.schema.ts` - 添加中文错误消息
- `allin-packages/disability-management-ui/src/components/DisabilityManagement.tsx` - 添加表单调试信息
- `allin-packages/disability-management-ui/src/api/types.ts` - 添加搜索相关类型定义
- `allin-packages/disability-management-ui/src/components/index.ts` - 导出残疾人选择器组件
- `allin-packages/disability-management-ui/tests/integration/disability.integration.test.tsx` - 修复测试选择器
- `allin-packages/disability-management-ui/tests/integration/disabled-person-selector.integration.test.tsx` - 移除lucide-react mock，修复API调用逻辑
- `packages/area-management-ui/src/types/area.ts` - 修复类型定义
- `docs/stories/008.005.transplant-disability-management-ui.story.md` - 更新开发记录

## QA Results
*Results from QA Agent QA review of the completed story implementation*