diff --git a/apps/assisted-ui/src/components/Chatbot.tsx b/apps/assisted-ui/src/components/Chatbot.tsx index 7e0eb260f9..39cb96af23 100644 --- a/apps/assisted-ui/src/components/Chatbot.tsx +++ b/apps/assisted-ui/src/components/Chatbot.tsx @@ -1,4 +1,6 @@ +import * as React from 'react'; import { ChatBot as AIChatBot, ChatBotWindowProps } from '@openshift-assisted/chatbot'; +import { useNavigate } from 'react-router-dom-v5-compat'; import '@patternfly-6/react-core/dist/styles/base.css'; import '@patternfly-6/chatbot/dist/css/main.css'; @@ -46,7 +48,8 @@ export const getOcmToken = async () => { }; const ChatBot = () => { - const onApiCall: ChatBotWindowProps['onApiCall'] = async (input, init) => { + const navigate = useNavigate(); + const onApiCall = React.useCallback(async (input, init) => { const token = await getOcmToken(); return fetch(`/chatbot${input.toString()}`, { ...(init || {}), @@ -55,9 +58,20 @@ const ChatBot = () => { Authorization: `Bearer ${token}`, }, }); - }; + }, []); - return ; + const openClusterDetails = React.useCallback( + (id) => navigate(`/assisted-installer/clusters/${id}`), + [navigate], + ); + + return ( + + ); }; export default ChatBot; diff --git a/libs/chatbot/lib/components/ChatBot/BotMessage.tsx b/libs/chatbot/lib/components/ChatBot/BotMessage.tsx index 3a99f6b767..82ae4463ec 100644 --- a/libs/chatbot/lib/components/ChatBot/BotMessage.tsx +++ b/libs/chatbot/lib/components/ChatBot/BotMessage.tsx @@ -4,7 +4,7 @@ import MessageLoading from '@patternfly-6/chatbot/dist/cjs/Message/MessageLoadin import { MsgProps } from './helpers'; import { Button, Stack, StackItem } from '@patternfly-6/react-core'; import { saveAs } from 'file-saver'; -import { DownloadIcon } from '@patternfly-6/react-icons'; +import { DownloadIcon, ExternalLinkAltIcon } from '@patternfly-6/react-icons'; import FeedbackForm from './FeedbackCard'; // eslint-disable-next-line @@ -25,6 +25,7 @@ export type BotMessageProps = { onApiCall: typeof fetch; conversationId: string | undefined; userMsg: string; + openClusterDetails: (clusterId: string) => void; }; const BotMessage = ({ @@ -35,6 +36,7 @@ const BotMessage = ({ isLastMsg, conversationId, userMsg, + openClusterDetails, }: BotMessageProps) => { const [openFeedback, setOpenFeedback] = React.useState(false); const [height, setHeight] = React.useState(initHeight); @@ -112,22 +114,33 @@ const BotMessage = ({ {isLoading && } {!isLoading && message.actions?.length && ( - {message.actions.map(({ title, url }, idx) => ( + {message.actions.map(({ title, url, clusterId }, idx) => ( - + {url && ( + + )} + {clusterId && ( + + )} ))} diff --git a/libs/chatbot/lib/components/ChatBot/ChatBot.tsx b/libs/chatbot/lib/components/ChatBot/ChatBot.tsx index aa8f26b45d..f23110ea78 100644 --- a/libs/chatbot/lib/components/ChatBot/ChatBot.tsx +++ b/libs/chatbot/lib/components/ChatBot/ChatBot.tsx @@ -2,13 +2,13 @@ import * as React from 'react'; import { ChatbotToggle } from '@patternfly-6/chatbot'; import ChatBotWindow, { ChatBotWindowProps } from './ChatBotWindow'; +import { useMessages } from '../../hooks/use-message'; import './Chatbot.css'; -import { useMessages } from '../../hooks/use-message'; -type ChatBotProps = Pick; +type ChatBotProps = Pick; -const ChatBot = ({ onApiCall, username }: ChatBotProps) => { +const ChatBot = ({ onApiCall, username, openClusterDetails }: ChatBotProps) => { const messagesProps = useMessages({ onApiCall, username, @@ -29,6 +29,7 @@ const ChatBot = ({ onApiCall, username }: ChatBotProps) => { }} onApiCall={onApiCall} username={username} + openClusterDetails={openClusterDetails} /> )} diff --git a/libs/chatbot/lib/components/ChatBot/ChatBotWindow.tsx b/libs/chatbot/lib/components/ChatBot/ChatBotWindow.tsx index ad224f4bf7..cb8d953c2b 100644 --- a/libs/chatbot/lib/components/ChatBot/ChatBotWindow.tsx +++ b/libs/chatbot/lib/components/ChatBot/ChatBotWindow.tsx @@ -35,6 +35,7 @@ export type ChatBotWindowProps = { startNewConversation: VoidFunction; isStreaming: boolean; announcement: string | undefined; + openClusterDetails: (clusterId: string) => void; }; const ChatBotWindow = ({ @@ -49,6 +50,7 @@ const ChatBotWindow = ({ announcement, error, resetError, + openClusterDetails, }: ChatBotWindowProps) => { const [triggerScroll, setTriggerScroll] = React.useState(0); const [msg, setMsg] = React.useState(''); @@ -145,6 +147,7 @@ const ChatBotWindow = ({ isLoading={index === messages.length - 1 && isStreaming} initHeight={isLastMsg ? getVisibleHeight() : undefined} isLastMsg={isLastMsg} + openClusterDetails={openClusterDetails} /> ); } diff --git a/libs/chatbot/lib/components/ChatBot/helpers.ts b/libs/chatbot/lib/components/ChatBot/helpers.ts index ff9845df82..708858fde2 100644 --- a/libs/chatbot/lib/components/ChatBot/helpers.ts +++ b/libs/chatbot/lib/components/ChatBot/helpers.ts @@ -1,9 +1,11 @@ import isString from 'lodash-es/isString.js'; import { Message } from '@patternfly-6/chatbot'; +type MsgAction = { title: string; url?: string; clusterId?: string }; + export type MsgProps = { pfProps: React.ComponentProps; - actions?: { title: string; url: string }[]; + actions?: MsgAction[]; }; export const MESSAGE_BAR_ID = 'assisted-chatbot__message-bar'; @@ -30,7 +32,7 @@ export const getToolAction = ({ toolName, response, args, -}: GetToolActionArgs): { title: string; url: string } | undefined => { +}: GetToolActionArgs): MsgAction | undefined => { switch (toolName) { case 'cluster_iso_download_url': { if (!response) { @@ -71,6 +73,23 @@ export const getToolAction = ({ }; } } + case 'install_cluster': { + if (args?.cluster_id) { + return { + title: 'Open cluster details page', + clusterId: args.cluster_id, + }; + } + } + case 'create_cluster': { + // TODO we need better response format to detect failures + if (response && !response.startsWith('Failed')) { + return { + title: 'Open cluster details page', + clusterId: response, + }; + } + } } return undefined; }; diff --git a/libs/chatbot/lib/hooks/use-message.ts b/libs/chatbot/lib/hooks/use-message.ts index 0998e5f1e6..f2d56f00f2 100644 --- a/libs/chatbot/lib/hooks/use-message.ts +++ b/libs/chatbot/lib/hooks/use-message.ts @@ -68,7 +68,6 @@ export const useMessages = ({ headers: { 'Content-Type': 'application/json', }, - credentials: 'include', }); if (!resp.ok) {