mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-06-11 09:55:59 +00:00
Keep unavailable channel connect buttons clickable
This commit is contained in:
@@ -34,10 +34,13 @@ function providerCanConnect(provider: ChannelProvider): boolean {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getProviderDisabledReason(
|
function getProviderUnavailableReason(
|
||||||
provider: ChannelProvider,
|
provider: ChannelProvider,
|
||||||
t: ReturnType<typeof useI18n>["t"],
|
t: ReturnType<typeof useI18n>["t"],
|
||||||
): string | undefined {
|
): string | undefined {
|
||||||
|
if (provider.unavailable_reason) {
|
||||||
|
return provider.unavailable_reason;
|
||||||
|
}
|
||||||
if (!provider.enabled) {
|
if (!provider.enabled) {
|
||||||
return t.channels.disabled;
|
return t.channels.disabled;
|
||||||
}
|
}
|
||||||
@@ -84,6 +87,7 @@ export function WorkspaceChannelsList() {
|
|||||||
connectMutation.isPending &&
|
connectMutation.isPending &&
|
||||||
connectMutation.variables === provider.provider;
|
connectMutation.variables === provider.provider;
|
||||||
const canConnect = providerCanConnect(provider);
|
const canConnect = providerCanConnect(provider);
|
||||||
|
const unavailableReason = getProviderUnavailableReason(provider, t);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SidebarMenuItem key={provider.provider}>
|
<SidebarMenuItem key={provider.provider}>
|
||||||
@@ -103,9 +107,14 @@ export function WorkspaceChannelsList() {
|
|||||||
"h-8 w-24 px-2 text-xs",
|
"h-8 w-24 px-2 text-xs",
|
||||||
isConnected && "gap-1",
|
isConnected && "gap-1",
|
||||||
)}
|
)}
|
||||||
disabled={!canConnect || isPending}
|
disabled={isConnected || isPending}
|
||||||
title={getProviderDisabledReason(provider, t)}
|
title={unavailableReason}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
if (!canConnect) {
|
||||||
|
toast.error(unavailableReason ?? t.channels.unavailable);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const connectWindow =
|
const connectWindow =
|
||||||
provider.auth_mode === "deep_link"
|
provider.auth_mode === "deep_link"
|
||||||
? prepareConnectWindow()
|
? prepareConnectWindow()
|
||||||
|
|||||||
@@ -81,10 +81,13 @@ function getStatusLabel(
|
|||||||
return t.channels.notConnected;
|
return t.channels.notConnected;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getProviderDisabledReason(
|
function getProviderUnavailableReason(
|
||||||
provider: ChannelProvider,
|
provider: ChannelProvider,
|
||||||
t: ReturnType<typeof useI18n>["t"],
|
t: ReturnType<typeof useI18n>["t"],
|
||||||
): string | undefined {
|
): string | undefined {
|
||||||
|
if (provider.unavailable_reason) {
|
||||||
|
return provider.unavailable_reason;
|
||||||
|
}
|
||||||
if (!provider.enabled) {
|
if (!provider.enabled) {
|
||||||
return t.channels.disabled;
|
return t.channels.disabled;
|
||||||
}
|
}
|
||||||
@@ -116,7 +119,7 @@ function ChannelProviderItem({
|
|||||||
disconnectMutation.variables === connection?.id;
|
disconnectMutation.variables === connection?.id;
|
||||||
const connectionLabel = connection ? getConnectionLabel(connection) : null;
|
const connectionLabel = connection ? getConnectionLabel(connection) : null;
|
||||||
const statusLabel = getStatusLabel(provider, connection, t);
|
const statusLabel = getStatusLabel(provider, connection, t);
|
||||||
const unavailableReason = getProviderDisabledReason(provider, t);
|
const unavailableReason = getProviderUnavailableReason(provider, t);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Item variant="outline" className="w-full items-start">
|
<Item variant="outline" className="w-full items-start">
|
||||||
@@ -162,9 +165,14 @@ function ChannelProviderItem({
|
|||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
size="sm"
|
size="sm"
|
||||||
disabled={!canConnect || isConnecting}
|
disabled={isConnecting}
|
||||||
title={unavailableReason}
|
title={unavailableReason}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
if (!canConnect) {
|
||||||
|
toast.error(unavailableReason ?? t.channels.unavailable);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const connectWindow =
|
const connectWindow =
|
||||||
provider.auth_mode === "deep_link"
|
provider.auth_mode === "deep_link"
|
||||||
? prepareConnectWindow()
|
? prepareConnectWindow()
|
||||||
|
|||||||
@@ -12,15 +12,19 @@ const channelProviders = [
|
|||||||
["wecom", "WeCom", "binding_code"],
|
["wecom", "WeCom", "binding_code"],
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
function mockChannelsAPI(page: Page) {
|
type MockChannelProvider = {
|
||||||
void page.route("**/api/channels/providers", (route) => {
|
provider: string;
|
||||||
return route.fulfill({
|
display_name: string;
|
||||||
status: 200,
|
enabled: boolean;
|
||||||
contentType: "application/json",
|
configured: boolean;
|
||||||
body: JSON.stringify({
|
connectable: boolean;
|
||||||
enabled: true,
|
auth_mode: string;
|
||||||
providers: channelProviders.map(
|
connection_status: string;
|
||||||
([provider, displayName, authMode]) => ({
|
unavailable_reason?: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
function defaultProviders(): MockChannelProvider[] {
|
||||||
|
return channelProviders.map(([provider, displayName, authMode]) => ({
|
||||||
provider,
|
provider,
|
||||||
display_name: displayName,
|
display_name: displayName,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
@@ -28,8 +32,21 @@ function mockChannelsAPI(page: Page) {
|
|||||||
connectable: true,
|
connectable: true,
|
||||||
auth_mode: authMode,
|
auth_mode: authMode,
|
||||||
connection_status: "not_connected",
|
connection_status: "not_connected",
|
||||||
}),
|
}));
|
||||||
),
|
}
|
||||||
|
|
||||||
|
function mockChannelsAPI(
|
||||||
|
page: Page,
|
||||||
|
providers: MockChannelProvider[] = defaultProviders(),
|
||||||
|
onSlackConnect?: () => void,
|
||||||
|
) {
|
||||||
|
void page.route("**/api/channels/providers", (route) => {
|
||||||
|
return route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
contentType: "application/json",
|
||||||
|
body: JSON.stringify({
|
||||||
|
enabled: true,
|
||||||
|
providers,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -43,6 +60,7 @@ function mockChannelsAPI(page: Page) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
void page.route("**/api/channels/slack/connect", (route) => {
|
void page.route("**/api/channels/slack/connect", (route) => {
|
||||||
|
onSlackConnect?.();
|
||||||
return route.fulfill({
|
return route.fulfill({
|
||||||
status: 200,
|
status: 200,
|
||||||
contentType: "application/json",
|
contentType: "application/json",
|
||||||
@@ -102,4 +120,42 @@ test.describe("IM channels", () => {
|
|||||||
page.getByText("Send /connect abc123 to the DeerFlow Slack bot."),
|
page.getByText("Send /connect abc123 to the DeerFlow Slack bot."),
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("unavailable providers stay clickable and explain what is missing", async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
mockLangGraphAPI(page);
|
||||||
|
const unavailableReason =
|
||||||
|
"Enable and configure channels.slack with channels.slack.bot_token and channels.slack.app_token.";
|
||||||
|
let connectRequests = 0;
|
||||||
|
mockChannelsAPI(
|
||||||
|
page,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
provider: "slack",
|
||||||
|
display_name: "Slack",
|
||||||
|
enabled: true,
|
||||||
|
configured: false,
|
||||||
|
connectable: false,
|
||||||
|
unavailable_reason: unavailableReason,
|
||||||
|
auth_mode: "binding_code",
|
||||||
|
connection_status: "not_connected",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
() => {
|
||||||
|
connectRequests += 1;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
await page.goto("/workspace/chats/new");
|
||||||
|
|
||||||
|
const sidebar = page.locator("[data-sidebar='sidebar']");
|
||||||
|
const connectButton = sidebar.getByRole("button", { name: "Connect" });
|
||||||
|
await expect(connectButton).toBeEnabled({ timeout: 15_000 });
|
||||||
|
|
||||||
|
await connectButton.click();
|
||||||
|
|
||||||
|
await expect(page.getByText(unavailableReason)).toBeVisible();
|
||||||
|
expect(connectRequests).toBe(0);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user