feat: watsonx Orchestrate deployment UI polish (#12621)

* feat: add credentials help link to deployment stepper and add-provider modal

Add a descriptive hint below the watsonx Orchestrate provider card in both
the deployment stepper and the add-provider modal, linking users to the IBM
docs to find their credentials.

* feat: add Beta badge to deployments tab and provider card

* fix: keep provider selector visible when selected provider has no deployments

The environment dropdown was hidden whenever deployments.length was 0,
stranding users on the empty state with no way to switch providers.

* feat: use watsonx Orchestrate logo in deployment provider UI

Replaces the generic Bot icon with a dedicated WatsonxOrchestrate SVG in
the deployment stepper and add-environment modal, wired through the
existing IBM icon barrel and the eager/lazy icon registries.

* fix: tighten Beta badge styling in main page header

Constrain height, padding, font size and color so the Beta badge
renders consistently alongside the tab label instead of inheriting
oversized default sizing.

* fix: rename "Deployment Providers" tab to "Deployment Environments"

Aligns the sub-tab label with the environment-centric terminology
introduced in #12551.
This commit is contained in:
Viktor Avelino
2026-04-10 15:58:16 -04:00
committed by GitHub
parent 1ddea3a0ff
commit 83f9b86d2a
10 changed files with 104 additions and 20 deletions

View File

@ -1,6 +1,7 @@
import React, { forwardRef } from "react";
import SvgIBM from "./ibm/IBM";
import SvgWatsonxAI from "./watsonx/WatsonxAI";
import SvgWatsonxOrchestrate from "./watsonx/WatsonxOrchestrate";
export const WatsonxAiIcon = forwardRef<
SVGSVGElement,
@ -9,6 +10,13 @@ export const WatsonxAiIcon = forwardRef<
return <SvgWatsonxAI ref={ref} {...props} />;
});
export const WatsonxOrchestrateIcon = forwardRef<
SVGSVGElement,
React.PropsWithChildren<{}>
>((props, ref) => {
return <SvgWatsonxOrchestrate ref={ref} {...props} />;
});
export const IBMIcon = forwardRef<SVGSVGElement, React.PropsWithChildren<{}>>(
(props, ref) => {
return <SvgIBM ref={ref} {...props} />;

View File

@ -0,0 +1,18 @@
const SvgWatsonxOrchestrate = (props) => (
<svg
fill={props.isDark ? "#ffffff" : "#0A0A0A"}
fillRule="evenodd"
style={{ flex: "none", lineHeight: 1 }}
viewBox="0 0 32 32"
width="1em"
height="1em"
{...props}
>
<path d="m27.9248,19.4282c-1.123,1.6104-2.9641,2.5718-4.9248,2.5718-2.1233,0-4.0464-1.0874-5.1438-2.9092l-1.7129,1.0322c1.4624,2.4277,4.0256,3.877,6.8567,3.877,1.4231,0,2.7925-.3911,3.9922-1.0835-2.3442,3.6958-6.4814,6.0835-10.9922,6.0835-5.7104,0-10.8347-3.8184-12.4617-9.2852l-1.917.5703c1.8774,6.3086,7.79,10.7148,14.3787,10.7148s12.5012-4.4062,14.3787-10.7148l-2.4539-.8569Z" />
<circle cx="30" cy="16" r="2" />
<circle cx="2" cy="16" r="2" />
<circle cx="16" cy="16" r="2" />
<path d="m16,1C9.4114,1,3.4988,5.4062,1.6213,11.7148l2.4539.8569c1.123-1.6104,2.9641-2.5718,4.9248-2.5718s3.8018.9614,4.9248,2.5718l1.6406-1.144c-1.4971-2.1465-3.9514-3.4277-6.5654-3.4277-1.4231,0-2.7925.3911-3.9922,1.0835,2.3442-3.6953,6.4814-6.0835,10.9922-6.0835,5.7104,0,10.8347,3.8184,12.4617,9.2852l1.917-.5703c-1.8774-6.3086-7.79-10.7148-14.3787-10.7148Z" />
</svg>
);
export default SvgWatsonxOrchestrate;

View File

@ -52,7 +52,7 @@ import { HCDIcon } from "@/icons/HCD";
import { HomeAssistantIcon } from "@/icons/HomeAssistant";
import { HuggingFaceIcon } from "@/icons/HuggingFace";
import { HackerNewsIcon } from "@/icons/hackerNews";
import { IBMIcon, WatsonxAiIcon } from "@/icons/IBM";
import { IBMIcon, WatsonxAiIcon, WatsonxOrchestrateIcon } from "@/icons/IBM";
import { IcosaIcon } from "@/icons/Icosa";
import { IFixIcon } from "@/icons/IFixIt";
import { JSIcon } from "@/icons/JSicon";
@ -235,6 +235,7 @@ export const eagerIconsMapping = {
VertexAI: VertexAIIcon,
vLLM: VllmIcon,
WatsonxAI: WatsonxAiIcon,
WatsonxOrchestrate: WatsonxOrchestrateIcon,
Weaviate: WeaviateIcon,
Wikipedia: WikipediaIcon,
Windsurf: WindsurfIcon,

View File

@ -290,6 +290,10 @@ export const lazyIconsMapping = {
import("@/icons/IBM").then((mod) => ({
default: mod.WatsonxAiIcon,
})),
WatsonxOrchestrate: () =>
import("@/icons/IBM").then((mod) => ({
default: mod.WatsonxOrchestrateIcon,
})),
Icosa: () =>
import("@/icons/Icosa").then((mod) => ({ default: mod.IcosaIcon })),
IFixIt: () =>

View File

@ -3,6 +3,7 @@ import { useCallback, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import ForwardedIconComponent from "@/components/common/genericIconComponent";
import ShadTooltip from "@/components/common/shadTooltipComponent";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { SidebarTrigger } from "@/components/ui/sidebar";
@ -151,10 +152,24 @@ const HeaderComponent = ({
: "border-border text-muted-foreground hover:text-foreground"
} text-nowrap px-2 pb-2 pt-1 text-mmd`}
>
<div className={flowType === type ? "-mb-px" : ""}>
<div
className={cn(
"flex items-center gap-1.5",
flowType === type && "-mb-px",
)}
>
{type === "mcp"
? t("mainPage.mcpServer")
: type.charAt(0).toUpperCase() + type.slice(1)}
{type === "deployments" && (
<Badge
variant="purpleStatic"
size="xq"
className="h-auto shrink-0 rounded px-1 py-px text-[11px] leading-none text-accent-purple-foreground"
>
Beta
</Badge>
)}
</div>
</Button>
))}

View File

@ -251,7 +251,7 @@ describe("Create mode — step navigation", () => {
id: "watsonx",
type: "watsonx",
name: "watsonx Orchestrate",
icon: "Bot",
icon: "WatsonxOrchestrate",
},
initialInstance: makeInstance(),
});

View File

@ -1,5 +1,6 @@
import { useState } from "react";
import ForwardedIconComponent from "@/components/common/genericIconComponent";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import {
Dialog,
@ -71,17 +72,35 @@ export default function AddProviderModal({
<DialogTitle data-testid="add-provider-modal-title">
Add Environment
</DialogTitle>
<DialogDescription>
Configure your watsonx Orchestrate credentials below.
<DialogDescription className="sr-only">
Add a new watsonx Orchestrate environment.
</DialogDescription>
<div className="flex flex-col gap-4 pt-2">
<div className="flex items-center gap-3 rounded-lg border border-border bg-muted p-3">
<ForwardedIconComponent
name="Bot"
className="h-8 w-8 text-foreground"
/>
<span className="text-sm font-medium">watsonx Orchestrate</span>
<div className="flex flex-col gap-3">
<div className="flex items-center gap-3 rounded-lg border border-border bg-muted p-3">
<ForwardedIconComponent
name="WatsonxOrchestrate"
className="h-8 w-8 text-foreground"
/>
<span className="text-sm font-medium">watsonx Orchestrate</span>
<Badge variant="purpleStatic" size="xq" className="shrink-0">
Beta
</Badge>
</div>
<p className="text-sm text-muted-foreground">
Configure your watsonx Orchestrate credentials below. Sign in or
sign up to{" "}
<a
href="https://www.ibm.com/docs/en/watsonx/watson-orchestrate/base?topic=api-getting-started"
target="_blank"
rel="noopener noreferrer"
className="font-medium text-primary hover:underline"
>
find your credentials
</a>
.
</p>
</div>
<ProviderCredentialsForm

View File

@ -80,7 +80,7 @@ export default function DeploymentsContent({
return (
<>
{providers.length >= 1 && deployments.length > 0 && (
{providers.length >= 1 && (
<div className="flex items-center gap-2">
<span className="text-sm text-muted-foreground">Environment:</span>
<Select

View File

@ -1,5 +1,6 @@
import { useEffect, useState } from "react";
import ForwardedIconComponent from "@/components/common/genericIconComponent";
import { Badge } from "@/components/ui/badge";
import { useGetProviderAccounts } from "@/controllers/API/queries/deployment-provider-accounts/use-get-provider-accounts";
import { cn } from "@/utils/utils";
import { useDeploymentStepper } from "../contexts/deployment-stepper-context";
@ -12,7 +13,7 @@ const PROVIDERS: DeploymentProvider[] = [
id: "watsonx",
type: "watsonx",
name: "watsonx Orchestrate",
icon: "Bot",
icon: "WatsonxOrchestrate",
},
];
@ -126,12 +127,30 @@ export default function StepProvider() {
<div className="flex h-full w-full flex-col gap-6 overflow-y-auto py-3">
<h2 className="text-lg font-semibold">Provider</h2>
<div className="flex items-center gap-3 rounded-lg border border-border bg-muted p-3">
<ForwardedIconComponent
name={provider.icon}
className="h-8 w-8 text-foreground"
/>
<span className="text-sm font-medium">{provider.name}</span>
<div className="flex flex-col gap-3">
<div className="flex items-center gap-3 rounded-lg border border-border bg-muted p-3">
<ForwardedIconComponent
name={provider.icon}
className="h-8 w-8 text-foreground"
/>
<span className="text-sm font-medium">{provider.name}</span>
<Badge variant="purpleStatic" size="xq" className="shrink-0">
Beta
</Badge>
</div>
<p className="text-sm text-muted-foreground">
Configure your watsonx Orchestrate credentials below. Sign in or sign
up to{" "}
<a
href="https://www.ibm.com/docs/en/watsonx/watson-orchestrate/base?topic=api-getting-started"
target="_blank"
rel="noopener noreferrer"
className="font-medium text-primary hover:underline"
>
find your credentials
</a>
.
</p>
</div>
{hasEnvironments ? (

View File

@ -10,7 +10,7 @@ interface SubTabToggleProps {
const tabs: { value: DeploymentSubTab; label: string }[] = [
{ value: "deployments", label: "Deployments" },
{ value: "providers", label: "Deployment Providers" },
{ value: "providers", label: "Deployment Environments" },
];
export default function SubTabToggle({