mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-16 13:03:31 +08:00
fix: reduce Feishu IM setup dead ends
Feishu onboarding previously expected users to find the template bot flow in docs before the desktop form made sense. Surface the documented OpenClaw creation link and the two required setup steps directly in the unconfigured Feishu settings state, while hiding the prompt once saved credentials exist. Constraint: The documented one-click template URL is the source of truth for bot creation. Rejected: Add a full wizard | this flow only needs a short external creation link plus credential fields. Confidence: high Scope-risk: narrow Directive: Keep this prompt short; detailed Feishu menu setup belongs in docs, not the settings form. Tested: cd desktop && bunx vitest run src/pages/AdapterSettings.test.tsx src/i18n/index.test.tsx Tested: cd desktop && bun run lint Tested: git diff --check Tested: bun run check:desktop Not-tested: Live Feishu developer console account flow.
This commit is contained in:
parent
d7315df18b
commit
51e67f20ff
@ -376,6 +376,11 @@ export const en = {
|
||||
'settings.adapters.appIdPlaceholder': 'e.g. cli_xxx',
|
||||
'settings.adapters.appSecret': 'App Secret',
|
||||
'settings.adapters.appSecretPlaceholder': 'From Feishu Open Platform',
|
||||
'settings.adapters.feishuCreateBotTitle': 'Need a Feishu bot?',
|
||||
'settings.adapters.feishuCreateBotDesc': 'Use the documented OpenClaw template to create a Feishu bot with the required permissions already configured. Then paste the App ID and App Secret below.',
|
||||
'settings.adapters.feishuCreateBotAction': 'Create Feishu bot',
|
||||
'settings.adapters.feishuCreateBotStepCreate': 'Create the bot from the template.',
|
||||
'settings.adapters.feishuCreateBotStepFill': 'Copy its App ID and App Secret, then fill them in here.',
|
||||
'settings.adapters.encryptKey': 'Encrypt Key',
|
||||
'settings.adapters.encryptKeyPlaceholder': 'Optional',
|
||||
'settings.adapters.verificationToken': 'Verification Token',
|
||||
|
||||
@ -378,6 +378,11 @@ export const zh: Record<TranslationKey, string> = {
|
||||
'settings.adapters.appIdPlaceholder': '如 cli_xxx',
|
||||
'settings.adapters.appSecret': 'App Secret',
|
||||
'settings.adapters.appSecretPlaceholder': '从飞书开放平台获取',
|
||||
'settings.adapters.feishuCreateBotTitle': '还没有飞书机器人?',
|
||||
'settings.adapters.feishuCreateBotDesc': '使用文档中的官方 OpenClaw 模板,一键创建已预配权限的飞书机器人。创建后把 App ID 和 App Secret 填到下方。',
|
||||
'settings.adapters.feishuCreateBotAction': '一键创建飞书机器人',
|
||||
'settings.adapters.feishuCreateBotStepCreate': '点击模板创建机器人。',
|
||||
'settings.adapters.feishuCreateBotStepFill': '复制 App ID 和 App Secret,回到这里填写。',
|
||||
'settings.adapters.encryptKey': 'Encrypt Key',
|
||||
'settings.adapters.encryptKeyPlaceholder': '可选',
|
||||
'settings.adapters.verificationToken': 'Verification Token',
|
||||
|
||||
54
desktop/src/pages/AdapterSettings.test.tsx
Normal file
54
desktop/src/pages/AdapterSettings.test.tsx
Normal file
@ -0,0 +1,54 @@
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { cleanup, render, screen } from '@testing-library/react'
|
||||
import '@testing-library/jest-dom'
|
||||
|
||||
import { AdapterSettings } from './AdapterSettings'
|
||||
import { useAdapterStore } from '../stores/adapterStore'
|
||||
import { useSettingsStore } from '../stores/settingsStore'
|
||||
import type { AdapterFileConfig } from '../types/adapter'
|
||||
|
||||
const FEISHU_CREATE_BOT_URL = 'https://open.feishu.cn/page/openclaw?form=multiAgent'
|
||||
|
||||
function renderAdapterSettings(config: AdapterFileConfig) {
|
||||
useSettingsStore.setState({ locale: 'en' })
|
||||
useAdapterStore.setState({
|
||||
config,
|
||||
isLoading: false,
|
||||
fetchConfig: vi.fn(async () => {}),
|
||||
} as Partial<ReturnType<typeof useAdapterStore.getState>>)
|
||||
|
||||
render(<AdapterSettings />)
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
cleanup()
|
||||
useAdapterStore.setState(useAdapterStore.getInitialState(), true)
|
||||
useSettingsStore.setState(useSettingsStore.getInitialState(), true)
|
||||
})
|
||||
|
||||
describe('AdapterSettings Feishu onboarding', () => {
|
||||
it('shows the documented one-click Feishu bot link before credentials are configured', () => {
|
||||
renderAdapterSettings({})
|
||||
|
||||
expect(screen.getByText('Need a Feishu bot?')).toBeInTheDocument()
|
||||
expect(screen.getByText(/OpenClaw template/)).toBeInTheDocument()
|
||||
expect(screen.getByText('1. Create the bot from the template.')).toBeInTheDocument()
|
||||
expect(screen.getByText('2. Copy its App ID and App Secret, then fill them in here.')).toBeInTheDocument()
|
||||
expect(screen.getByRole('link', { name: /create feishu bot/i })).toHaveAttribute(
|
||||
'href',
|
||||
FEISHU_CREATE_BOT_URL,
|
||||
)
|
||||
})
|
||||
|
||||
it('hides the one-click Feishu bot prompt once saved credentials exist', () => {
|
||||
renderAdapterSettings({
|
||||
feishu: {
|
||||
appId: 'cli_existing',
|
||||
appSecret: '****cret',
|
||||
},
|
||||
})
|
||||
|
||||
expect(screen.queryByRole('link', { name: /create feishu bot/i })).not.toBeInTheDocument()
|
||||
expect(screen.queryByText('Need a Feishu bot?')).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
@ -10,6 +10,8 @@ import QRCode from 'qrcode'
|
||||
type ImTab = 'feishu' | 'wechat' | 'dingtalk' | 'telegram'
|
||||
type ImPlatform = 'telegram' | 'feishu' | 'wechat' | 'dingtalk'
|
||||
|
||||
const FEISHU_CREATE_BOT_URL = 'https://open.feishu.cn/page/openclaw?form=multiAgent'
|
||||
|
||||
export function AdapterSettings() {
|
||||
const t = useTranslation()
|
||||
const {
|
||||
@ -375,6 +377,7 @@ export function AdapterSettings() {
|
||||
const pairingExpiry = config.pairing?.expiresAt
|
||||
const isPairingActive = pairingExpiry ? Date.now() < pairingExpiry : false
|
||||
const minutesLeft = pairingExpiry ? Math.max(0, Math.ceil((pairingExpiry - Date.now()) / 60000)) : 0
|
||||
const hasSavedFeishuCredentials = Boolean(config.feishu?.appId && config.feishu?.appSecret)
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
@ -505,6 +508,32 @@ export function AdapterSettings() {
|
||||
|
||||
{activeIm === 'feishu' && (
|
||||
<div className="p-4 space-y-4">
|
||||
{!hasSavedFeishuCredentials && (
|
||||
<div className="rounded-lg border border-[var(--color-border)] bg-[var(--color-surface)] p-4">
|
||||
<div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
|
||||
<div className="flex min-w-0 gap-3">
|
||||
<span className="material-symbols-outlined mt-0.5 text-[20px] text-[var(--color-brand)]">smart_toy</span>
|
||||
<div className="min-w-0">
|
||||
<h4 className="text-sm font-semibold text-[var(--color-text-primary)]">{t('settings.adapters.feishuCreateBotTitle')}</h4>
|
||||
<p className="mt-1 text-xs leading-5 text-[var(--color-text-tertiary)]">{t('settings.adapters.feishuCreateBotDesc')}</p>
|
||||
<ol className="mt-2 space-y-1 text-xs leading-5 text-[var(--color-text-secondary)]">
|
||||
<li>1. {t('settings.adapters.feishuCreateBotStepCreate')}</li>
|
||||
<li>2. {t('settings.adapters.feishuCreateBotStepFill')}</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
<a
|
||||
href={FEISHU_CREATE_BOT_URL}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex h-9 shrink-0 items-center justify-center gap-1.5 rounded-[var(--radius-md)] bg-[image:var(--gradient-btn-primary)] px-3 text-xs font-medium text-[var(--color-btn-primary-fg)] shadow-[var(--shadow-button-primary)] transition-colors hover:bg-[image:var(--gradient-btn-primary-hover)] hover:brightness-105 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-brand)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--color-surface)]"
|
||||
>
|
||||
{t('settings.adapters.feishuCreateBotAction')}
|
||||
<span className="material-symbols-outlined text-[14px]">open_in_new</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<Input
|
||||
label={t('settings.adapters.appId')}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user