diff --git a/openspec/changes/add-im-platform-binding-ui/design.md b/openspec/changes/add-im-platform-binding-ui/design.md index 758e55c8783..0f1d9f12862 100644 --- a/openspec/changes/add-im-platform-binding-ui/design.md +++ b/openspec/changes/add-im-platform-binding-ui/design.md @@ -5,14 +5,14 @@ 已有 `hitl-im-contact-domain-discovery` change 已确认以下领域约束,本 change 直接复用而不重新定义: - 同一 Organization 一期只启用一个 IM platform。 -- EE 由企业管理员管理,CE / SaaS 由 workspace owner 或 workspace admin 管理。 +- 该 workspace 入口只面向非企业版;CE / SaaS 由 workspace owner 或 workspace admin 管理,enterprise plan 不展示本入口。 - 连接状态包含 `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 挂载。 +当前 web 中的 `web/features/agent-v2/roster/` 是 AI Agent 资产管理,不能作为 Contacts UI 的实现位置。Contacts feature 需要拥有自己的组件、数据访问抽象和路由边界,并由非企业版 CE / SaaS workspace 设置页挂载。 设计验收来源: @@ -26,7 +26,7 @@ **Goals:** - 在 Contacts 管理区域提供从未绑定、配置、mock 授权、连接异常到已连接的完整 IM platform 管理体验。 -- 通过同一套 Contacts feature UI 适配 EE 企业管理面和 CE / SaaS workspace 管理面。 +- 在非 enterprise plan 的 CE / SaaS workspace 设置页提供 Contacts feature UI,并在 enterprise plan 隐藏入口。 - 支持手动启动 mock 通讯录同步、恢复 mock 进行中任务、查看最新摘要和指定 sync run 详情。 - 清晰表达 matched、created binding、updated binding、unmatched、skipped、failed 等结果,帮助管理员理解联系人映射。 - 将数据访问集中在可替换的 typed repository,避免页面组件直接依赖 fixture shape。 @@ -46,7 +46,7 @@ ### 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 和返回路径。 +实现建立 Contacts-owned feature 模块,包含 binding summary、provider setup、connection diagnostics、sync trigger 和 sync details。CE / SaaS workspace 设置页负责使用现有 plan 判断、提供 Organization context、mock permission context 和返回路径;enterprise plan 不挂载该入口。 AI Agent Roster 与 Human Contacts 的数据、权限和生命周期完全不同,因此不复用 `web/features/agent-v2/roster/`。 @@ -170,7 +170,7 @@ repository 暴露读取 integration/provider、保存配置、mock 授权、测 - [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 内容保持单一实现。 +- [企业版误展示 mock 入口] → workspace shell 复用现有 `Plan.enterprise` 判断,深链同样回退到允许访问的 tab。 - [不可控 polling 造成测试不稳定] → mock 状态迁移使用 fake timers 或显式推进,不使用随机延迟。 - [大型详情 fixture 降低测试性能] → 用小型分页 fixture 验证增量加载,不生成大规模浏览器内数据。 - [Secret 泄露到 fixture 或快照] → fixture 只保存配置标记,repository 丢弃输入文本,测试断言日志与 DOM 中无 secret。 diff --git a/openspec/changes/add-im-platform-binding-ui/proposal.md b/openspec/changes/add-im-platform-binding-ui/proposal.md index c68c6bd8535..31cc4674f90 100644 --- a/openspec/changes/add-im-platform-binding-ui/proposal.md +++ b/openspec/changes/add-im-platform-binding-ui/proposal.md @@ -28,7 +28,7 @@ Contacts 目前缺少用于配置 Organization 级 IM platform、触发通讯录 - 前端需要在 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 提供,仅用于前端行为展示,不构成安全边界。 +- 管理入口只在非企业版的 CE / SaaS workspace 管理面展示,并复用现有 workspace plan 判断隐藏 enterprise plan;角色权限、provider availability 和各类状态暂由 mock scenario 提供,仅用于前端行为展示,不构成安全边界。 - 需要为权限、连接状态、表单提交、手动同步、匹配结果和同步详情补充 Vitest / Testing Library 测试,并使用确定性的 mock scenario 完成前端 smoke 验证。 - 后续后端能力就绪时,应通过新的 change 用真实 repository adapter 替换 mock repository,而不改写页面组件的状态语义。 - 设计验收来源: 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 index 632a28f2431..b0d28388cd6 100644 --- 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 @@ -43,18 +43,18 @@ - **WHEN** 用户保存、测试、授权、替换或解除绑定 - **THEN** 当前 change 的实现 MUST 只调用 mock repository,MUST NOT 新增后端 endpoint、OpenAPI schema 或网络请求 -### Requirement: 管理入口必须展示部署形态与权限差异 +### Requirement: 管理入口必须只在非企业版 workspace 展示 -前端 MUST 支持 EE 企业管理面和 CE / SaaS workspace 管理面的 Contacts IM platform 入口。EE scenario 由企业管理员管理;CE / SaaS scenario 由 workspace owner 或 workspace admin 管理。该权限结果 MUST 来自 mock context,且 MUST 被明确视为 UI 展示状态而非真实安全边界。 +前端 MUST 只在非 enterprise plan 的 CE / SaaS workspace 管理面展示 Contacts IM platform 入口,并 MUST 复用现有 workspace plan 判断。非企业版由 workspace owner 或 workspace admin 管理;权限结果 MUST 来自 mock context,且 MUST 被明确视为 UI 展示状态而非真实安全边界。 -#### Scenario: EE 企业管理员管理绑定 +#### Scenario: 企业版不展示绑定入口 -- **WHEN** EE 企业管理员 scenario 从企业 Contacts 管理面进入 IM platform 设置 -- **THEN** 前端 MUST 允许其查看和操作当前 Organization 的 mock 绑定 +- **WHEN** 当前 workspace plan 为 enterprise +- **THEN** 前端 MUST 不展示 Contacts IM platform 入口,且通过深链请求该 tab 时 MUST 回退到允许访问的设置页 -#### Scenario: CE 或 SaaS 管理员管理绑定 +#### Scenario: 非企业版管理员管理绑定 -- **WHEN** CE / SaaS workspace owner 或 workspace admin scenario 从 workspace Contacts 管理面进入 IM platform 设置 +- **WHEN** 非 enterprise plan 的 CE / SaaS workspace owner 或 workspace admin 从 workspace Contacts 管理面进入 IM platform 设置 - **THEN** 前端 MUST 允许其查看和操作当前 Organization 的 mock 绑定 #### Scenario: 无管理权限 diff --git a/openspec/changes/add-im-platform-binding-ui/tasks.md b/openspec/changes/add-im-platform-binding-ui/tasks.md index db58b57f6c4..2c0a042ff7c 100644 --- a/openspec/changes/add-im-platform-binding-ui/tasks.md +++ b/openspec/changes/add-im-platform-binding-ui/tasks.md @@ -1,6 +1,6 @@ ## 1. Frontend Scope and Design Baseline -- [x] 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/`. +- [x] 1.1 Identify the CE / SaaS workspace-management mount point and existing enterprise-plan guard for Contacts, define the Organization context contract, and verify that no entry is added under `web/features/agent-v2/roster/`. - [x] 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. - [x] 1.3 Define the existing product feature gate used to expose the mock-backed Contacts IM platform entry and its rollback behavior. @@ -14,7 +14,7 @@ ## 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.1 Write failing component tests for the non-enterprise Contacts entry, enterprise-plan exclusion, 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. diff --git a/web/features/contacts/im-platform/README.md b/web/features/contacts/im-platform/README.md index 9a670779e23..8bccd8615f8 100644 --- a/web/features/contacts/im-platform/README.md +++ b/web/features/contacts/im-platform/README.md @@ -5,10 +5,10 @@ Organization-scoped IM platform binding and directory-sync UI for the Contacts d ## Mount Points - CE / SaaS workspace management mounts this feature from `app/components/header/account-setting`. -- The open-source web tree does not contain a separate EE enterprise-management shell. The feature therefore exposes an Organization context boundary with an `enterprise` scope so that the EE shell can mount the same surface without importing workspace UI. +- Enterprise-plan workspaces do not expose this binding entry. The Account Settings shell checks `ProviderContext.plan.type` before mounting the feature. - `features/agent-v2/roster` is intentionally outside this feature boundary and must not import or mount Contacts IM platform UI. -The entry is guarded by the existing `NEXT_PUBLIC_ENABLE_FEATURE_PREVIEW` product gate. Setting the flag to `false` removes the mock-backed entry without deleting saved frontend code or affecting Contacts data. +The entry requires both a non-enterprise workspace plan and the existing `NEXT_PUBLIC_ENABLE_FEATURE_PREVIEW` product gate. Setting the flag to `false`, or changing the workspace to the enterprise plan, removes the mock-backed entry without deleting saved frontend code or affecting Contacts data. ## Internal Modules diff --git a/web/features/contacts/im-platform/__tests__/feature-flag.spec.ts b/web/features/contacts/im-platform/__tests__/feature-flag.spec.ts index dc29675a8f7..0a9ccb04a5e 100644 --- a/web/features/contacts/im-platform/__tests__/feature-flag.spec.ts +++ b/web/features/contacts/im-platform/__tests__/feature-flag.spec.ts @@ -11,13 +11,20 @@ describe('Contacts IM platform feature flag', () => { featurePreviewEnabled = false }) - it('uses the existing feature-preview product gate', async () => { + it('uses the existing feature-preview product gate for non-enterprise workspaces', async () => { const { isContactsImPlatformEnabled } = await import('../feature-flag') - expect(isContactsImPlatformEnabled()).toBe(false) + expect(isContactsImPlatformEnabled(false)).toBe(false) featurePreviewEnabled = true - expect(isContactsImPlatformEnabled()).toBe(true) + expect(isContactsImPlatformEnabled(false)).toBe(true) + }) + + it('keeps the entry hidden for enterprise workspaces', async () => { + featurePreviewEnabled = true + const { isContactsImPlatformEnabled } = await import('../feature-flag') + + expect(isContactsImPlatformEnabled(true)).toBe(false) }) }) diff --git a/web/features/contacts/im-platform/context.ts b/web/features/contacts/im-platform/context.ts index 58be89b8056..08deafb005f 100644 --- a/web/features/contacts/im-platform/context.ts +++ b/web/features/contacts/im-platform/context.ts @@ -1,14 +1,5 @@ -export const ContactsImPlatformManagementScope = { - Enterprise: 'enterprise', - Workspace: 'workspace', -} as const - -export type ContactsImPlatformManagementScope = - (typeof ContactsImPlatformManagementScope)[keyof typeof ContactsImPlatformManagementScope] - export type ContactsImPlatformOrganizationContext = { - organizationId: string - workspaceId?: string - managementScope: ContactsImPlatformManagementScope canManage: boolean + organizationId: string + workspaceId: string } diff --git a/web/features/contacts/im-platform/feature-flag.ts b/web/features/contacts/im-platform/feature-flag.ts index cc4ba4f7f0a..5b4e51239f3 100644 --- a/web/features/contacts/im-platform/feature-flag.ts +++ b/web/features/contacts/im-platform/feature-flag.ts @@ -1,3 +1,4 @@ import { ENABLE_FEATURE_PREVIEW } from '@/config' -export const isContactsImPlatformEnabled = () => ENABLE_FEATURE_PREVIEW +export const isContactsImPlatformEnabled = (isEnterprisePlan: boolean) => + ENABLE_FEATURE_PREVIEW && !isEnterprisePlan