diff --git a/openspec/changes/add-im-platform-binding-ui/.openspec.yaml b/openspec/changes/add-im-platform-binding-ui/.openspec.yaml new file mode 100644 index 00000000000..64105fc96f1 --- /dev/null +++ b/openspec/changes/add-im-platform-binding-ui/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-07-14 diff --git a/openspec/changes/add-im-platform-binding-ui/design.md b/openspec/changes/add-im-platform-binding-ui/design.md new file mode 100644 index 00000000000..758e55c8783 --- /dev/null +++ b/openspec/changes/add-im-platform-binding-ui/design.md @@ -0,0 +1,193 @@ +## Context + +本 change 只覆盖 Contacts 领域中的 Organization 级 IM platform 绑定与通讯录同步详情。它不涉及 Agent 发布、Agent Roster、App access point 或 workflow node binding。Figma 文件虽然名为 “Agent Roster”,但用户已经明确这些节点表达的是 Contacts 的 IM platform 管理 UI。 + +已有 `hitl-im-contact-domain-discovery` change 已确认以下领域约束,本 change 直接复用而不重新定义: + +- 同一 Organization 一期只启用一个 IM platform。 +- EE 由企业管理员管理,CE / SaaS 由 workspace owner 或 workspace admin 管理。 +- 连接状态包含 `Not configured`、`Configured`、`Connected`、`Permission issue`、`Callback error`、`Connection error`。 +- 通讯录同步由管理员手动触发,不做自动同步。 +- 同步优先按 platform user ID 匹配已有 binding,再按 Email 匹配 Contact;未命中对象进入 unmatched,不能自动创建 External contact。 + +本 change 的实现范围严格限定为前端。后端 contract 尚未就绪,因此页面、查询、mutation、权限、provider availability、同步任务和同步详情均由集中、类型安全、确定性的 mock repository 驱动。后续真实 API 接入必须通过独立 change 完成。 + +当前 web 中的 `web/features/agent-v2/roster/` 是 AI Agent 资产管理,不能作为 Contacts UI 的实现位置。Contacts feature 需要拥有自己的组件、数据访问抽象和路由边界,并由 EE 与 CE / SaaS 的不同管理 shell 挂载。 + +设计验收来源: + +| 范围 | Figma node | +| -------- | -------------------------------------------------- | +| 绑定界面 | `1649:6572`、`1613:5906`、`1646:5214`、`1646:5959` | +| 同步详情 | `1634:5098`、`1634:5104` | + +## Goals / Non-Goals + +**Goals:** + +- 在 Contacts 管理区域提供从未绑定、配置、mock 授权、连接异常到已连接的完整 IM platform 管理体验。 +- 通过同一套 Contacts feature UI 适配 EE 企业管理面和 CE / SaaS workspace 管理面。 +- 支持手动启动 mock 通讯录同步、恢复 mock 进行中任务、查看最新摘要和指定 sync run 详情。 +- 清晰表达 matched、created binding、updated binding、unmatched、skipped、failed 等结果,帮助管理员理解联系人映射。 +- 将数据访问集中在可替换的 typed repository,避免页面组件直接依赖 fixture shape。 +- 遵循 i18n、dify-ui、可访问性和前端测试约束。 + +**Non-Goals:** + +- 不实现或修改 Agent、Agent Roster、Agent access point 或 workflow Agent binding。 +- 不在本 change 中实现 Contact CRUD、External contact 创建、workspace IM override 或单个 Contact 的 IM identity 编辑。 +- 不实现 unmatched 的手动映射、忽略或转 Contact 操作;同步详情保持只读。 +- 不修改后端 API、OpenAPI schema、生成式 client、领域模型、数据库迁移、Celery task、provider adapter 或 credential 加密逻辑。 +- 不执行真实 OAuth、真实 provider callback、真实网络连接测试、真实目录同步或服务端权限校验。 +- 不支持同一 Organization 同时启用多个 IM platform。 +- 不建设完整同步历史审计产品;只展示当前进行中的任务、最近结果和由 `sync_run_id` 指定的 mock 详情。 + +## Decisions + +### 1. Contacts feature 独立拥有 UI,并由不同管理 shell 挂载 + +实现建立 Contacts-owned feature 模块,包含 binding summary、provider setup、connection diagnostics、sync trigger 和 sync details。EE 企业管理面与 CE / SaaS workspace 管理面只负责提供 Organization context、mock permission context 和返回路径。 + +AI Agent Roster 与 Human Contacts 的数据、权限和生命周期完全不同,因此不复用 `web/features/agent-v2/roster/`。 + +### 2. Typed mock repository 是唯一数据边界 + +组件不得直接 import 零散 JSON fixture,也不得在组件内按场景硬编码响应。feature 定义稳定的前端 view model 和 repository interface,例如: + +```text +ContactIMIntegrationView + organization_id + provider + status + safe_status_reason + last_checked_at + can_manage + capabilities.directory_sync + secret_configured + last_sync + +ContactIMProviderDefinition + provider + display_name + availability + unavailable_reason + auth_mode + callback_url + required_fields + +ContactIMSyncRunView + id + status + started_at + started_by + completed_at + counts + safe_error + +ContactIMSyncItemView + id + result + platform_identity + matched_contact + safe_reason +``` + +repository 暴露读取 integration/provider、保存配置、mock 授权、测试连接、解除绑定、启动同步、读取 active run 和分页读取详情等方法。mock 实现通过命名 scenario 或 seed 创建状态,所有延迟、错误和状态迁移必须确定且可在测试中控制,不能依赖随机数或不可控的真实计时。 + +后续真实后端就绪时,应新增实现相同 interface 的 API repository adapter,再由 composition root 切换依赖;页面组件和业务状态语义不应因此改写。 + +### 3. 使用共享绑定 shell 与 provider-specific form adapter + +绑定 dialog / drawer 共享标题、状态、footer、错误反馈和 callback 区域;每个 provider 使用类型明确的 form adapter 处理自己的字段、帮助文案和认证方式。provider availability 和 capability 暂由 mock provider definition 提供。 + +不采用完全动态 JSON form renderer,因为 App ID、OAuth、callback、权限说明等交互差异较大;也不为每个 provider 复制完整 overlay,以避免状态和错误处理分叉。 + +### 4. 连接状态与 mutation 状态分层 + +`Not configured` 等六种状态来自 repository 中持久化的 mock integration state。`saving`、`testing`、`authorizing`、`disconnecting` 是短暂的前端 mutation state,不写入 connection status。 + +- 保存凭据成功后刷新 integration,mock repository 可将状态推进到 `Configured`。 +- 测试连接或 mock OAuth 结束后刷新 integration,由当前 scenario 决定最终六态。 +- 所有 mutation pending 时阻止重复提交。 +- 不做会伪造成功结果的 optimistic update。 + +### 5. Secret fixture 只表达配置状态,不保存真实 secret + +已配置的 mock 数据只包含 `secret_configured: true` 或等价标识,不包含可回显 secret。用户输入新 secret 时,repository 只记录“已替换”的结果或版本标识,并立即丢弃原始文本;测试不得打印或快照 secret。 + +编辑非 secret 字段时使用字段省略或明确的 retain-secret command,掩码文本不得进入 mutation payload。 + +### 6. Mock sync run 使用 mutation 启动、query 恢复和有限 polling + +启动同步 mutation 返回稳定的 `sync_run_id`。随后以该 ID 查询状态,只在 queued / running 时 polling;进入 success、partial success 或 failure 后停止 polling,并刷新 integration summary 和 detail query。 + +页面初始化时读取 mock active run。若 scenario 中存在进行中任务,UI 恢复该任务而不是创建新任务。mock repository 应允许测试通过 fake timers 或显式推进函数控制状态变化。 + +### 7. Sync details 以 `sync_run_id` 为稳定上下文 + +同步详情 surface 按 Figma 采用页面、drawer 或 dialog 的最终形态,但数据上下文必须由 `sync_run_id` 标识。推荐将该 ID 放入 URL query state,使刷新、返回和错误恢复不会丢失当前详情。 + +详情 query 支持 result filter 与 mock pagination。缺失 Email、Contact 或 platform name 时显示统一空值,不通过其他前端缓存猜测补全。 + +### 8. Summary 与 detail 使用统一结果 taxonomy + +- `matched`:已匹配且 binding 无需修改。 +- `created_binding`:创建新的 Contact IM binding。 +- `updated_binding`:更新已有 Contact IM binding。 +- `unmatched`:无法映射到 Contact,留待人工处理。 +- `skipped`:因重复、缺少必要字段或规则明确忽略。 +- `failed`:单条处理发生错误。 + +任务级状态与条目级分类分开:有成功条目同时也有 unmatched、skipped 或 failed 时,任务 UI 显示 partial success。 + +### 9. 权限和 provider gate 是 mock 展示状态,不是安全边界 + +入口 shell 根据 mock `can_manage` 和 provider capability 隐藏或禁用绑定、测试、同步和解除绑定操作,并展示相应解释。由于本 change 没有后端,权限场景只用于验收前端降级行为,不可被视为真实授权机制。 + +### 10. 使用 React Query、repository 与局部表单状态 + +- Integration、provider definitions、active sync 和 sync detail 通过 React Query 包装 repository 读取。 +- 绑定表单草稿仅由 overlay 局部状态拥有,不引入新的全局 store。 +- mutation 成功后按 query key 精确失效。 +- overlay 使用 `@langgenius/dify-ui/*` primitives。 +- mock repository 由 feature composition 层注入,生产构建不发出后端请求。 + +### 11. 测试围绕可观察状态机与替换边界 + +前端测试至少覆盖: + +- 无绑定、已配置、已连接和三类错误状态。 +- credential 与 mock OAuth 两种认证路径。 +- secret 不回显、掩码不提交、更新时保留原 secret。 +- 无权限、provider 不可用、保存 / 测试失败和重复提交。 +- 同步按钮 gate、active run 恢复、polling 停止、成功 / 部分成功 / 失败摘要。 +- 详情筛选、mock 分页、加载失败、unmatched 只读和敏感错误脱敏。 +- 关键 overlay 的焦点恢复、键盘提交和错误关联。 +- 页面只通过 repository interface 访问数据,替换 repository implementation 不改变组件 contract。 + +## Risks / Trade-offs + +- [Mock shape 与未来 API 漂移] → 以 UI 所需 view model 为稳定边界;真实 adapter 负责映射后端 DTO,而不是让组件依赖后端 shape。 +- [Mock 交互被误认为真实功能] → 入口使用 feature gate,并在交付说明中明确没有后端持久化、真实授权和真实同步。 +- [Figma 中 provider-specific 差异可能继续调整] → 保留共享 shell 与 provider adapter 边界,将视觉差异限制在 adapter 内。 +- [EE 与 CE / SaaS 入口不同导致重复实现] → shell 只注入 context,feature 内容保持单一实现。 +- [不可控 polling 造成测试不稳定] → mock 状态迁移使用 fake timers 或显式推进,不使用随机延迟。 +- [大型详情 fixture 降低测试性能] → 用小型分页 fixture 验证增量加载,不生成大规模浏览器内数据。 +- [Secret 泄露到 fixture 或快照] → fixture 只保存配置标记,repository 丢弃输入文本,测试断言日志与 DOM 中无 secret。 + +## Migration Plan + +1. 建立 Contacts-owned view model、repository interface、query keys 和命名 mock scenarios。 +2. 在 mock repository 上完成绑定、连接状态、手动同步与同步详情 UI。 +3. 完成 Vitest / Testing Library 覆盖、Figma 对照、i18n、可访问性和前端 smoke。 +4. 通过现有产品 feature gate 控制入口开放范围。 + +本 change 不包含数据迁移,也不要求任何后端部署步骤。回滚时关闭或移除前端入口即可。 + +后端能力就绪后另建 change:实现真实 contract 与 API repository adapter、补充服务端权限和 credential 安全,并将 composition root 从 mock 切换为真实 adapter。 + +## Open Questions + +- 首个前端 mock 需要展示哪些 provider、认证方式和 directory sync capability,需在实现时依据 Figma 可见内容确定。 +- 替换或解除 provider 的最终数据影响必须由后续 Contacts / IM 后端 contract 决定;当前 mock 只用于展示确认交互。 +- Figma 中 callback、权限说明和 provider 帮助链接的最终文案,需要在实现阶段通过已授权的 Figma 访问核对。 diff --git a/openspec/changes/add-im-platform-binding-ui/proposal.md b/openspec/changes/add-im-platform-binding-ui/proposal.md new file mode 100644 index 00000000000..c68c6bd8535 --- /dev/null +++ b/openspec/changes/add-im-platform-binding-ui/proposal.md @@ -0,0 +1,36 @@ +## Why + +Contacts 目前缺少用于配置 Organization 级 IM platform、触发通讯录同步以及排查同步结果的完整管理界面,管理员无法在产品内完成从“绑定渠道”到“确认联系人映射结果”的闭环。设计稿已经定义了绑定与同步详情的主要交互,需要将其整理为可实施、可验收的规格。 + +## What Changes + +- 在 Contacts 管理区域增加 IM platform 管理入口,展示当前 provider、连接状态、最近同步信息和可执行操作。 +- 提供首次绑定、补全配置、重新连接和更新配置的交互流程,并覆盖 provider 特定字段、表单校验、保存中状态与失败反馈。 +- 将连接状态映射为 `Not configured`、`Configured`、`Connected`、`Permission issue`、`Callback error`、`Connection error`,为异常状态提供可排查原因和恢复入口。 +- 支持由具备权限的管理员手动触发 IM 联系人同步,并展示同步进行中、成功、部分成功和失败状态。 +- 增加同步详情视图,展示同步时间、发起人、结果汇总以及 matched、updated、unmatched、skipped、failed 等明细。 +- 补齐权限受限、空数据、加载、重复提交、mock mutation 失败和重试等 UI 状态,并确保 secret 不被回显或写入前端日志。 +- 本 change 只实现前端:所有展示与交互先通过集中、类型安全、可替换的 mock repository 驱动;真实后端 contract 与 API 接入留给后续独立 change。 +- 以用户提供的六个 Figma 节点作为布局、文案层级和交互验收基准。 + +## Capabilities + +### New Capabilities + +- `contact-im-platform-binding`: 管理员在 Contacts 中查看 IM platform 状态、完成 provider 绑定或更新配置,并从异常状态恢复连接。 +- `contact-im-directory-sync-details`: 管理员手动同步 IM 通讯录并查看一次同步的汇总、逐项匹配结果、异常原因和后续处理入口。 + +### Modified Capabilities + +无。 + +## Impact + +- 前端需要在 Contacts feature 边界内实现管理界面、相关路由、typed mock repository、dify-ui 组件使用和 `web/i18n/*` 文案。现有 `web/features/agent-v2/roster/` 管理的是可复用 AI Agent 资产,不属于本 change。 +- 本 change 不修改后端 API、OpenAPI schema、生成式 client、数据模型、数据库迁移、任务队列、provider adapter 或真实 OAuth / credential 存储逻辑。 +- 管理入口需要适配部署形态:EE 企业管理面与 CE / SaaS workspace 管理面复用同一 Contacts feature UI;角色权限、provider availability 和各类状态暂由 mock scenario 提供,仅用于前端行为展示,不构成安全边界。 +- 需要为权限、连接状态、表单提交、手动同步、匹配结果和同步详情补充 Vitest / Testing Library 测试,并使用确定性的 mock scenario 完成前端 smoke 验证。 +- 后续后端能力就绪时,应通过新的 change 用真实 repository adapter 替换 mock repository,而不改写页面组件的状态语义。 +- 设计验收来源: + - 绑定界面:Figma nodes `1649:6572`、`1613:5906`、`1646:5214`、`1646:5959` + - 同步详情:Figma nodes `1634:5098`、`1634:5104` diff --git a/openspec/changes/add-im-platform-binding-ui/specs/contact-im-directory-sync-details/spec.md b/openspec/changes/add-im-platform-binding-ui/specs/contact-im-directory-sync-details/spec.md new file mode 100644 index 00000000000..f8ece130d29 --- /dev/null +++ b/openspec/changes/add-im-platform-binding-ui/specs/contact-im-directory-sync-details/spec.md @@ -0,0 +1,207 @@ +## ADDED Requirements + +### Requirement: Contacts 通讯录同步必须由管理员手动触发 + +前端 MUST 只允许 mock context 中具备管理权限的用户手动触发 Contacts IM directory sync。同步入口 MUST 仅在当前 IM platform 为 `Connected` 且 provider definition 声明支持通讯录读取时可用;本 capability MUST NOT 引入定时或自动同步。 + +#### Scenario: 已连接且支持通讯录同步 + +- **WHEN** 当前 provider 状态为 `Connected`、支持 directory sync,且 `can_manage` 为 true +- **THEN** 前端 MUST 启用手动同步操作 + +#### Scenario: Provider 尚未连接 + +- **WHEN** 当前 provider 未配置、仅为 `Configured` 或处于任一错误状态 +- **THEN** 前端 MUST 禁用同步操作并提示先完成或修复连接 + +#### Scenario: Provider 不支持通讯录同步 + +- **WHEN** mock provider capability 声明不支持 directory sync +- **THEN** 前端 MUST 禁止触发同步并展示能力限制说明 + +#### Scenario: 普通用户尝试同步 + +- **WHEN** mock context 中无管理权限的用户进入同步区域 +- **THEN** 前端 MUST 隐藏或禁用同步操作并展示权限说明 + +### Requirement: 同步 UI 必须只通过 typed mock repository 访问数据 + +本 change 中的同步 mutation、状态 query、摘要和详情 MUST 通过 Contacts-owned repository interface 访问 typed mock data。组件 MUST NOT 调用真实同步 API、真实 provider 或生成式 client。 + +#### Scenario: 启动 mock 同步 + +- **WHEN** 管理员点击同步 +- **THEN** 前端 MUST 调用 repository mutation 并取得稳定的 mock `sync_run_id` + +#### Scenario: 读取 mock 同步状态 + +- **WHEN** 页面展示 active run、最新摘要或指定 run 详情 +- **THEN** 前端 MUST 通过 repository view model 读取数据,MUST NOT 直接 import 详情 fixture + +#### Scenario: 确定性状态推进 + +- **WHEN** queued 或 running scenario 需要进入终态 +- **THEN** repository MUST 使用 fake timers 或显式推进机制控制状态,MUST NOT 使用随机结果或不可控的真实延迟 + +#### Scenario: 前端同步不访问真实服务 + +- **WHEN** 任一同步交互运行 +- **THEN** 当前 change MUST NOT 新增后端 endpoint、OpenAPI schema、任务队列或网络请求 + +### Requirement: 同一 Organization 不得重复启动并行 mock 同步 + +当前 Organization 已存在 queued 或 running 的 mock sync run 时,前端 MUST 连接到该任务的状态,MUST NOT 再创建重复任务。 + +#### Scenario: 启动新的同步 + +- **WHEN** 当前没有进行中的 run 且管理员点击同步 +- **THEN** repository MUST 创建一次 mock run、返回其 identifier,并使 UI 进入进行中状态 + +#### Scenario: 重复点击同步 + +- **WHEN** 同步 mutation 或当前 run 仍在进行中 +- **THEN** 前端 MUST 禁用重复触发并继续展示当前 run 状态 + +#### Scenario: 页面重新打开时已有同步 + +- **WHEN** 管理员重新打开 Contacts,而当前 scenario 已有 queued 或 running run +- **THEN** 前端 MUST 恢复该 run 的进度展示,MUST NOT 将其视为新的同步 + +### Requirement: UI 必须展示同步生命周期与最新摘要 + +前端 MUST 区分 queued、running、success、partial success 和 failure,并 MUST 展示 mock view model 提供的发起时间、发起人、完成时间或耗时、结果计数和安全错误摘要。同步状态 MUST 与 IM connection status 分开表达。 + +#### Scenario: 同步进行中 + +- **WHEN** mock run 为 queued 或 running +- **THEN** 前端 MUST 展示进行中状态,并按受控间隔读取该 run + +#### Scenario: 同步全部成功 + +- **WHEN** mock run 完成且没有 unmatched、skipped 或 failed 结果 +- **THEN** 前端 MUST 展示 success、完成时间和结果汇总 + +#### Scenario: 同步部分成功 + +- **WHEN** mock run 完成且同时包含成功结果与 unmatched、skipped 或 failed 结果 +- **THEN** 前端 MUST 展示 partial success,并引导管理员查看同步详情 + +#### Scenario: 同步失败 + +- **WHEN** scenario 将 run 推进为 failure +- **THEN** 前端 MUST 展示安全错误原因和重试入口,同时保留上一次已完成 run 的可查看结果 + +#### Scenario: 同步到达终态 + +- **WHEN** queued 或 running run 进入任一终态 +- **THEN** 前端 MUST 停止 polling,并刷新 integration summary 和当前 run 详情 + +### Requirement: 同步摘要必须区分联系人匹配结果 + +前端 MUST 使用同一个 mock sync run 的计数展示 matched、created binding、updated binding、unmatched、skipped 和 failed。前端 MUST NOT 将不同 run 的结果拼接为同一次摘要。 + +#### Scenario: 展示同步结果计数 + +- **WHEN** 一次 mock run 已返回结果摘要 +- **THEN** 前端 MUST 展示该 run 各结果分类的计数与总量 + +#### Scenario: 结果计数为零 + +- **WHEN** 某个结果分类的计数为零 +- **THEN** 前端 MUST 以明确零值或设计指定的省略规则展示,MUST NOT 将其显示为未知错误 + +#### Scenario: 计数与详情不一致 + +- **WHEN** 开发 fixture 中的摘要计数与当前 run 详情不一致 +- **THEN** mock scenario validation SHOULD 使测试失败,避免把自相矛盾的数据交给 UI + +### Requirement: 同步详情必须提供逐项、可排查的匹配信息 + +前端 MUST 允许管理员打开指定 `sync_run_id` 的详情。每条 mock 结果 MUST 在数据可用时展示 IM platform identity 的显示名、Email、platform user ID、匹配到的 Contact、结果分类和安全原因;缺失字段 MUST 使用明确空值表达,不得伪造数据。 + +#### Scenario: 已匹配 Contact + +- **WHEN** mock platform member 已匹配到 Contact +- **THEN** 详情 MUST 展示 platform identity、目标 Contact 以及 matched、created binding 或 updated binding 结果 + +#### Scenario: 未匹配 platform member + +- **WHEN** mock platform member 未命中 binding 或 Contact +- **THEN** 详情 MUST 将其标记为 unmatched,MUST NOT 表示系统已自动创建 External contact + +#### Scenario: 跳过条目 + +- **WHEN** mock item 因缺少必要字段、重复或规则明确忽略而被跳过 +- **THEN** 详情 MUST 展示 skipped 状态和安全原因 + +#### Scenario: 单条处理失败 + +- **WHEN** mock item 的 result 为 failed +- **THEN** 详情 MUST 展示 failed 状态和可排查原因,同时保留其他成功条目 + +#### Scenario: 通过 URL 恢复详情 + +- **WHEN** URL query 中包含有效 `sync_run_id` +- **THEN** 前端 MUST 恢复该 run 的详情上下文 + +### Requirement: 同步详情必须支持结果筛选与 mock 分页 + +前端 MUST 支持按结果分类查看同步条目,并 MUST 使用 repository 提供的分页或等价增量加载方式展示详情。筛选、分页和重试 MUST 始终绑定到当前 `sync_run_id`。 + +#### Scenario: 按 unmatched 筛选 + +- **WHEN** 管理员选择 unmatched 分类 +- **THEN** 前端 MUST 只读取或展示当前 run 中的 unmatched 条目 + +#### Scenario: 加载更多同步条目 + +- **WHEN** 当前分类还有下一页 mock 结果 +- **THEN** 前端 MUST 允许增量加载且不得重复已有条目 + +#### Scenario: 详情分页失败 + +- **WHEN** scenario 使后续页读取失败 +- **THEN** 前端 MUST 保留已加载条目并提供针对当前页的重试操作 + +#### Scenario: 切换筛选项 + +- **WHEN** 管理员从一个结果分类切换到另一个分类 +- **THEN** 前端 MUST 重置该分类的分页游标,且 MUST NOT 混入上一分类的条目 + +### Requirement: Unmatched 结果必须保持 Contacts 领域语义 + +前端 MUST 将 unmatched 结果作为待后续人工处理的同步事实展示。同步详情 MUST NOT 自动创建 External contact,也 MUST NOT 在没有明确后续 capability 的情况下修改 Contact 或 IM Binding。 + +#### Scenario: 查看 unmatched 详情 + +- **WHEN** 管理员打开 unmatched 条目 +- **THEN** 前端 MUST 展示 platform identity 和未匹配原因,MUST NOT 自动改变任何 Contacts 数据 + +#### Scenario: 后续处理能力尚未提供 + +- **WHEN** 当前版本没有手动映射或忽略 unmatched 的 capability +- **THEN** 前端 MUST 将详情保持为只读,MUST NOT 展示无法完成的伪操作 + +### Requirement: 同步 UI 必须安全、可访问且可恢复 + +前端和 mock fixture MUST 避免在同步摘要、详情、日志、错误反馈或测试快照中暴露 provider credential。用户可见文案 MUST 国际化,关键状态与操作 MUST 能被辅助技术识别。 + +#### Scenario: Mock 错误包含敏感诊断文本 + +- **WHEN** scenario 构造的原始错误包含 credential 或敏感 request detail +- **THEN** repository MUST 只向 view model 暴露经过安全处理的错误摘要 + +#### Scenario: 同步详情加载失败 + +- **WHEN** 指定 mock run 的详情读取失败 +- **THEN** 前端 MUST 展示错误与重试操作,并 MUST 保留可用的同步摘要 + +#### Scenario: 辅助技术读取状态变化 + +- **WHEN** run 从 queued 进入 running 或终态 +- **THEN** 前端 MUST 以不会造成重复噪声的可访问方式通知关键状态变化 + +#### Scenario: 关闭同步详情后恢复焦点 + +- **WHEN** 管理员关闭同步详情 drawer 或 dialog +- **THEN** 前端 MUST 将焦点恢复到打开详情的触发控件 diff --git a/openspec/changes/add-im-platform-binding-ui/specs/contact-im-platform-binding/spec.md b/openspec/changes/add-im-platform-binding-ui/specs/contact-im-platform-binding/spec.md new file mode 100644 index 00000000000..632a28f2431 --- /dev/null +++ b/openspec/changes/add-im-platform-binding-ui/specs/contact-im-platform-binding/spec.md @@ -0,0 +1,202 @@ +## ADDED Requirements + +### Requirement: Contacts 必须提供 Organization 级 IM platform 绑定入口 + +前端 MUST 在 Contacts 管理区域展示当前 Organization 的 IM platform 绑定状态与管理入口。该入口只用于联系人目录、联系人 IM identity 与通讯录同步,MUST NOT 被解释为 Agent、App 或 workflow 的渠道绑定。 + +#### Scenario: 尚未绑定 IM platform + +- **WHEN** 具备 mock 管理权限的用户打开 Contacts,且当前 scenario 没有 IM platform 配置 +- **THEN** 前端 MUST 展示未绑定状态和开始绑定操作 + +#### Scenario: 已绑定 IM platform + +- **WHEN** 当前 scenario 已有 IM platform 配置 +- **THEN** 前端 MUST 展示 provider、连接状态、最近检查信息、最近同步摘要以及当前可执行操作 + +#### Scenario: Agent Roster 不承载该入口 + +- **WHEN** 用户浏览可复用 AI Agent 的 Agent Roster +- **THEN** 前端 MUST NOT 在该列表中展示或管理本 capability 的联系人 IM platform 绑定 + +### Requirement: 绑定 UI 必须只通过 typed mock repository 访问数据 + +本 change 中的绑定页面、query 和 mutation MUST 通过 Contacts-owned repository interface 访问类型安全的 mock 数据。组件 MUST NOT 直接 import 零散 fixture、调用真实 IM provider、调用后端 API 或依赖生成式 API client。 + +#### Scenario: 初始读取绑定状态 + +- **WHEN** 绑定页面初始化 +- **THEN** 前端 MUST 通过 repository 读取 integration、provider definition、permission 和 capability view model + +#### Scenario: 切换验收场景 + +- **WHEN** 测试或开发预览选择一个命名 mock scenario +- **THEN** repository MUST 确定性返回该场景对应的状态、错误和可执行操作 + +#### Scenario: 未来替换数据实现 + +- **WHEN** 后续 change 提供真实 API repository adapter +- **THEN** 页面组件的 props、query keys 和业务状态语义 SHOULD 无需改写即可切换 adapter + +#### Scenario: 前端操作不访问真实服务 + +- **WHEN** 用户保存、测试、授权、替换或解除绑定 +- **THEN** 当前 change 的实现 MUST 只调用 mock repository,MUST NOT 新增后端 endpoint、OpenAPI schema 或网络请求 + +### Requirement: 管理入口必须展示部署形态与权限差异 + +前端 MUST 支持 EE 企业管理面和 CE / SaaS workspace 管理面的 Contacts IM platform 入口。EE scenario 由企业管理员管理;CE / SaaS scenario 由 workspace owner 或 workspace admin 管理。该权限结果 MUST 来自 mock context,且 MUST 被明确视为 UI 展示状态而非真实安全边界。 + +#### Scenario: EE 企业管理员管理绑定 + +- **WHEN** EE 企业管理员 scenario 从企业 Contacts 管理面进入 IM platform 设置 +- **THEN** 前端 MUST 允许其查看和操作当前 Organization 的 mock 绑定 + +#### Scenario: CE 或 SaaS 管理员管理绑定 + +- **WHEN** CE / SaaS workspace owner 或 workspace admin scenario 从 workspace Contacts 管理面进入 IM platform 设置 +- **THEN** 前端 MUST 允许其查看和操作当前 Organization 的 mock 绑定 + +#### Scenario: 无管理权限 + +- **WHEN** 当前 mock context 的 `can_manage` 为 false +- **THEN** 前端 MUST 隐藏或禁用写操作,并 MUST 展示明确的权限说明 + +#### Scenario: 权限状态读取失败 + +- **WHEN** mock repository 返回权限读取失败 +- **THEN** 前端 MUST 展示专用错误和重试操作,MUST NOT 将其降级为 `Not configured` + +### Requirement: 同一 Organization 同时只能展示一个 active IM platform + +前端 MUST 将当前 Organization 的 IM platform 作为单选绑定能力管理。已有 mock 绑定时,前端 MUST 展示该 provider 的配置,MUST NOT 在未确认替换或解除现有绑定的情况下创建第二个 active provider。 + +#### Scenario: 首次选择 provider + +- **WHEN** 当前 scenario 尚未配置 IM platform,且管理员选择一个可用 provider +- **THEN** 前端 MUST 打开该 provider 对应的绑定流程 + +#### Scenario: 已有 active provider + +- **WHEN** 当前 scenario 已绑定一个 provider +- **THEN** 前端 MUST 将该 provider 作为当前唯一 active binding 展示 + +#### Scenario: 替换 provider + +- **WHEN** 管理员尝试将当前 provider 替换为另一个 provider +- **THEN** 前端 MUST 在执行 mock mutation 前要求确认,并 MUST 展示 scenario 提供的影响说明 + +### Requirement: 绑定流程必须适配 mock provider definition + +前端 MUST 根据 typed mock provider definition 展示可用 provider、认证方式、必填字段、callback 信息和能力说明。凭据型 provider MUST 使用 provider-specific 表单;OAuth 型 provider MUST 使用可控的 mock 授权流程。 + +#### Scenario: 配置凭据型 provider + +- **WHEN** 管理员选择使用 App ID、App Secret 或同类凭据的 provider +- **THEN** 前端 MUST 展示该 provider 所需字段、必填校验、帮助说明和可复制的 callback 信息 + +#### Scenario: 配置 OAuth 型 provider + +- **WHEN** 管理员选择 OAuth 型 provider 并开始授权 +- **THEN** 前端 MUST 进入 mock authorization pending 状态,并在 scenario 返回后刷新 repository 中的绑定状态 + +#### Scenario: Provider 当前不可用 + +- **WHEN** mock provider definition 将某个 provider 标记为未发布、不受当前部署支持或缺少必要能力 +- **THEN** 前端 MUST 禁止开始该 provider 的绑定,并 MUST 展示可理解的不可用原因 + +#### Scenario: 必填字段缺失 + +- **WHEN** 管理员提交凭据表单但缺少 provider 要求的字段 +- **THEN** 前端 MUST 阻止 mutation 并在对应字段附近展示校验信息 + +### Requirement: Secret 必须在 mock 绑定 UI 中保持不可回显 + +前端和 mock fixture MUST NOT 保存、返回或预填可回显的真实 secret。已有绑定只能通过 `secret_configured` 或等价状态表达。若管理员未提供替换值,前端 MUST NOT 把掩码或占位符作为新 secret 传给 repository。 + +#### Scenario: 打开已有绑定配置 + +- **WHEN** 管理员打开一个已配置 App Secret 的 mock provider +- **THEN** DOM、fixture、日志、错误反馈和测试快照 MUST NOT 包含原 secret + +#### Scenario: 更新非 secret 字段 + +- **WHEN** 管理员只修改非 secret 字段并保留原 secret +- **THEN** 前端 MUST 省略替换值或发送 typed retain-secret command,MUST NOT 发送掩码文本 + +#### Scenario: 替换 secret + +- **WHEN** 管理员显式输入新的 secret 并执行保存 +- **THEN** mock repository MUST 只记录“secret 已替换”的状态并立即丢弃输入文本,提交完成后 UI MUST 恢复为不可回显状态 + +### Requirement: UI 必须完整表达六种 IM connection status + +前端 MUST 使用 mock repository state 区分 `Not configured`、`Configured`、`Connected`、`Permission issue`、`Callback error`、`Connection error`。错误状态 MUST 展示可安全公开的原因、最近检查时间以及适用的恢复操作。 + +#### Scenario: 配置已保存但尚未验证 + +- **WHEN** mock provider 配置已保存但尚未完成连接测试 +- **THEN** 前端 MUST 展示 `Configured`,MUST NOT 将其误报为 `Connected` + +#### Scenario: Mock 连接测试成功 + +- **WHEN** 当前 scenario 的测试连接 mutation 成功 +- **THEN** repository MUST 将状态推进为 `Connected`,前端 MUST 刷新并展示新的可用操作 + +#### Scenario: Provider 权限不足 + +- **WHEN** repository 返回 `Permission issue` +- **THEN** 前端 MUST 展示缺失权限或修复指引,并 MUST 提供重新测试或更新配置入口 + +#### Scenario: Callback 配置异常 + +- **WHEN** repository 返回 `Callback error` +- **THEN** 前端 MUST 展示 callback 相关原因和可复制的正确 callback 信息 + +#### Scenario: 其他连接失败 + +- **WHEN** repository 返回 `Connection error` +- **THEN** 前端 MUST 展示安全的失败原因和重试入口,MUST NOT 暴露凭据 + +### Requirement: 保存、测试、更新与解除绑定必须防止重复操作 + +前端 MUST 为保存配置、mock 测试连接、更新配置、替换 provider 和解除绑定提供明确的 pending、成功与失败状态。操作进行中 MUST 防止重复 mutation;失败时 MUST 保留可安全保留的表单内容,并通过重新读取 repository 确认最终状态。 + +#### Scenario: 保存进行中 + +- **WHEN** mock 保存 mutation 尚未结束 +- **THEN** 前端 MUST 禁用会产生重复写入的操作并展示进行中状态 + +#### Scenario: 保存失败 + +- **WHEN** scenario 配置保存 mutation 失败 +- **THEN** 前端 MUST 保持表单可继续修正,展示安全失败原因,MUST NOT 乐观展示成功状态 + +#### Scenario: 测试连接 + +- **WHEN** 管理员触发 mock 测试连接 +- **THEN** 前端 MUST 防止并发重复测试,并 MUST 在 mutation 结束后重新读取 connection status + +#### Scenario: 解除绑定 + +- **WHEN** 管理员确认解除当前 IM platform 绑定,且 mock mutation 成功 +- **THEN** 前端 MUST 展示 `Not configured` 并关闭新的通讯录同步入口 + +### Requirement: 绑定 UI 必须具备完整的基础交互状态 + +前端 MUST 为初始加载、加载失败、空状态、表单错误、权限受限和操作成功提供可访问且国际化的反馈。用户可见文案 MUST 使用项目 i18n 资源,交互控件 MUST 支持键盘操作与可辨识的 focus 状态。 + +#### Scenario: 初始数据加载失败 + +- **WHEN** mock repository 返回 Contacts IM platform 状态加载失败 +- **THEN** 前端 MUST 展示错误状态和重试操作,MUST NOT 将失败误显示为未绑定 + +#### Scenario: 键盘完成绑定 + +- **WHEN** 管理员只使用键盘浏览 provider、填写表单并提交 +- **THEN** 前端 MUST 保持合理的焦点顺序、可见焦点和可读错误关联 + +#### Scenario: Overlay 关闭后恢复焦点 + +- **WHEN** 管理员关闭 binding dialog 或 drawer +- **THEN** 前端 MUST 将焦点恢复到打开该 overlay 的触发控件 diff --git a/openspec/changes/add-im-platform-binding-ui/tasks.md b/openspec/changes/add-im-platform-binding-ui/tasks.md new file mode 100644 index 00000000000..038ecaf71a6 --- /dev/null +++ b/openspec/changes/add-im-platform-binding-ui/tasks.md @@ -0,0 +1,36 @@ +## 1. Frontend Scope and Design Baseline + +- [ ] 1.1 Identify the EE enterprise-management and CE / SaaS workspace-management mount points for Contacts, define the shared Organization context contract, and verify that no entry is added under `web/features/agent-v2/roster/`. +- [ ] 1.2 Inspect the six referenced Figma nodes with authorized access and record a frontend acceptance matrix for layout, overlay type, fields, statuses, table columns, responsive behavior, and visible copy. +- [ ] 1.3 Define the existing product feature gate used to expose the mock-backed Contacts IM platform entry and its rollback behavior. + +## 2. Typed Mock Data Boundary + +- [ ] 2.1 Write failing unit tests for repository scenario consistency, single-active-provider behavior, deterministic state transitions, summary/detail count agreement, and secret sanitization. +- [ ] 2.2 Add Contacts-owned TypeScript view models, command types, repository interface, query keys, and typed result taxonomy for integration, provider definitions, sync runs, and sync items. +- [ ] 2.3 Implement named deterministic mock scenarios covering loading, load failure, no permission, provider unavailable, all six connection states, mutation failures, active sync, success, partial success, failure, detail failure, and paginated results. +- [ ] 2.4 Implement the in-memory mock repository so mutations update only mock state, queued/running transitions are controllable with fake timers or explicit advancement, and secret input is discarded after recording configuration state. +- [ ] 2.5 Add a feature composition boundary and React Query hooks that inject the repository, invalidate precise query keys after mutations, and make no backend or provider network requests. + +## 3. IM Platform Binding UI + +- [ ] 3.1 Write failing component tests for the Contacts entry, EE and CE / SaaS permission variants, initial loading/error/empty states, provider availability, and the six connection-status presentations. +- [ ] 3.2 Implement the shared Contacts IM platform management surface, status summary, provider selection, diagnostics, recent-sync summary, and feature-gated mount points with `@langgenius/dify-ui/*` primitives. +- [ ] 3.3 Write failing component tests for credential and mock OAuth flows, required-field errors, pending-state duplicate prevention, mutation failure recovery, provider replacement confirmation, and disconnect behavior. +- [ ] 3.4 Implement the shared binding overlay and typed provider-specific form adapters, including mock authorization recovery, callback copy interaction, replacement/disconnect confirmation, and repository refresh after mutations. +- [ ] 3.5 Write and satisfy security regression tests proving that configured secrets are represented only by a boolean/state marker and never appear in fixtures, DOM output, logs, snapshots, or retained mutation payloads. + +## 4. Manual Directory Sync and Details UI + +- [ ] 4.1 Write failing component and hook tests for sync eligibility, no-permission and unsupported-provider gates, duplicate-trigger prevention, active-run restoration, controlled polling, and polling termination at every terminal state. +- [ ] 4.2 Implement the manual sync trigger, queued/running presentation, success/partial-success/failure summaries, latest completed result retention, and targeted query refresh behavior. +- [ ] 4.3 Write failing component tests for `sync_run_id` URL restoration, result taxonomy and counts, missing-field placeholders, unmatched read-only behavior, filters, pagination, page retry, and sensitive-error sanitization. +- [ ] 4.4 Implement the Figma-aligned sync details surface with run metadata, result summary, filter controls, incrementally loaded rows, per-item safe reasons, error recovery, and no Contact or IM Binding mutation actions. + +## 5. Product Quality and Verification + +- [ ] 5.1 Add all user-facing copy to `web/i18n/en-US/` and update every supported locale with correct localized values. +- [ ] 5.2 Match the authorized Figma acceptance matrix using dify-ui tokens, including loading and error states, responsive layouts, visible focus, keyboard submission, error associations, live status announcements, and focus restoration for overlays. +- [ ] 5.3 Run the targeted Vitest / Testing Library suites and resolve all failures, including fake-timer cleanup and React Query cache isolation between scenarios. +- [ ] 5.4 Run the repository-prescribed frontend formatting, lint, and type-check commands, then fix issues introduced by this change. +- [ ] 5.5 Audit the final diff to confirm it changes only frontend and OpenSpec files, adds no backend/OpenAPI/generated-client/task-queue code, issues no real IM or backend requests, and leaves the future API repository adapter for a separate change.