diff --git a/libs/shared/devops-copilot/feature/src/lib/devops-copilot-panel/devops-copilot-panel.tsx b/libs/shared/devops-copilot/feature/src/lib/devops-copilot-panel/devops-copilot-panel.tsx index b5f75ca613d..5c5c9e2e000 100644 --- a/libs/shared/devops-copilot/feature/src/lib/devops-copilot-panel/devops-copilot-panel.tsx +++ b/libs/shared/devops-copilot/feature/src/lib/devops-copilot-panel/devops-copilot-panel.tsx @@ -3,7 +3,6 @@ import * as Dialog from '@radix-ui/react-dialog' import { ScrollArea } from '@radix-ui/react-scroll-area' import clsx from 'clsx' import mermaid from 'mermaid' -import { useFeatureFlagVariantKey } from 'posthog-js/react' import { type Cluster, type Environment, type Organization, type Project } from 'qovery-typescript-axios' import { type CSSProperties, useCallback, useEffect, useRef, useState } from 'react' import { match } from 'ts-pattern' @@ -74,7 +73,6 @@ export function DevopsCopilotPanel({ onClose, style }: DevopsCopilotPanelProps) const docLinks = useContextualDocLinks() const { context, current } = useQoveryContext() const { user, getAccessTokenSilently } = useAuth0() - const isDevopsCopilotPanelFeatureFlag = useFeatureFlagVariantKey('devops-copilot') const organizationId = context?.organization?.id ?? '' @@ -457,7 +455,7 @@ export function DevopsCopilotPanel({ onClose, style }: DevopsCopilotPanelProps) ref={panelRef} className={twMerge( clsx( - 'fixed bottom-2 right-2 z-[1] flex rounded-xl border border-neutral-200 bg-white shadow-[0_16px_70px_rgba(0,0,0,0.2)] dark:border-neutral-500 dark:bg-neutral-600', + 'fixed bottom-2 right-2 z-[1] flex overflow-hidden rounded-xl border border-neutral-200 bg-white shadow-[0_16px_70px_rgba(0,0,0,0.2)] dark:border-neutral-500 dark:bg-neutral-600', { 'left-4 top-4 animate-[scalein_0.22s_ease_both] opacity-0': expand, 'animate-slidein-up-sm-faded': !expand, @@ -498,288 +496,316 @@ export function DevopsCopilotPanel({ onClose, style }: DevopsCopilotPanelProps) setIsLoading={setIsLoading} setPlan={setPlan} /> -
- {!isReadOnly && ( -
- - - - - - Read-write mode enabled - - The copilot can perform actions on your infrastructure. - {thread.length === 0 && ' Mode cannot be changed after the conversation has started.'} - - - -
- )} - {thread.length === 0 && ( - - I'm your DevOps AI Copilot - I can help you to fix - your deployments, optimize your infrastructure costs, audit your security and do everything you would - expect from a complete DevOps Engineering team. - {isDevopsCopilotPanelFeatureFlag && !isCopilotEnabled && ( - - I'm not enabled yet,{' '} - - configure me in your organization settings - {' '} - to get started. - - )} - - )} - 0, - 'h-[calc(100vh-316px)]': expand && thread.length > 0, - }) - )} - > - {thread.length === 0 && docLinks.length > 0 && expand && isCopilotEnabled && ( -
- - - Ask for a contextual suggestion: - -
- {docLinks.map(({ label, link }) => ( - - ))} -
+ {isCopilotEnabled ? ( +
+ {!isReadOnly && ( +
+ + + + + + Read-write mode enabled + + The copilot can perform actions on your infrastructure. + {thread.length === 0 && ' Mode cannot be changed after the conversation has started.'} + + +
)} - {thread.map((message: Message) => { - return match(message.owner) - .with('user', () => ( + {thread.length === 0 && ( + + I'm your DevOps AI Copilot - I can help you to + fix your deployments, optimize your infrastructure costs, audit your security and do everything you + would expect from a complete DevOps Engineering team. + + )} + 0, + 'h-[calc(100vh-316px)]': expand && thread.length > 0, + }) + )} + > + {thread.length === 0 && docLinks.length > 0 && expand && isCopilotEnabled && ( +
+ + + Ask for a contextual suggestion: + +
+ {docLinks.map(({ label, link }) => ( + + ))} +
+
+ )} + {thread.map((message: Message) => { + return match(message.owner) + .with('user', () => ( +
+
{message.text}
+
+ )) + .with('assistant', () => ( + + )) + .exhaustive() + })} + {isLoading && streamingMessage.length === 0 && ( +
setShowPlans((prev) => ({ ...prev, temp: !prev['temp'] }))} > -
{message.text}
+ + {loadingText} + + {plan.filter((p) => p.messageId === 'temp').length > 0 && ( + + )}
- )) - .with('assistant', () => ( - - )) - .exhaustive() - })} - {isLoading && streamingMessage.length === 0 && ( -
-
setShowPlans((prev) => ({ ...prev, temp: !prev['temp'] }))} - > - {loadingText} - {plan.filter((p) => p.messageId === 'temp').length > 0 && ( - + {showPlans['temp'] && plan.filter((p) => p.messageId === 'temp').length > 0 && ( +
+ {plan + .filter((p) => p.messageId === 'temp') + .map((step, index) => ( +
+ +
+ + {step.description} + + {step.status.replace('_', ' ')} +
+
+ ))} +
)}
- {showPlans['temp'] && plan.filter((p) => p.messageId === 'temp').length > 0 && ( -
- {plan - .filter((p) => p.messageId === 'temp') - .map((step, index) => ( -
- -
- - {step.description} - - {step.status.replace('_', ' ')} -
-
- ))} -
+ )} + {isLoading && streamingMessage.length > 0 && pendingThreadId.current === threadId && ( + + )} +
+ {!isAtBottom && ( + )}
- )} - {isLoading && streamingMessage.length > 0 && pendingThreadId.current === threadId && ( - - )} -
- {!isAtBottom && ( - + +
0, + })} + > + {thread.length === 0 && docLinks.length > 0 && !expand && isCopilotEnabled && ( +
+ + Ask for a contextual suggestion: + +
+ {docLinks.map(({ label, link }) => ( + + ))} +
+
)} -
- -
0, - })} - > - {thread.length === 0 && docLinks.length > 0 && !expand && isCopilotEnabled && ( -
- - Ask for a contextual suggestion: - -
- {docLinks.map(({ label, link }) => ( +
+ {withContext && ( +
+ + + + + {upperCaseFirstLetter(String(current?.type))}:{' '} + {String(current?.name ?? 'No context')} + + + - ))} -
+
+ )} + setInputMessage(e.target.value)} + onClick={handleSendMessage} + onKeyDown={(e) => { + if (e.key === 'Enter' && !e.shiftKey && !isLoading) { + e.preventDefault() + handleSendMessage() + } else { + if (inputRef.current) adjustTextareaHeight(inputRef.current) + } + }} + onInput={(e) => adjustTextareaHeight(e.target as HTMLTextAreaElement)} + loading={isLoading} + stop={() => { + setIsStopped(true) + setIsFinish(true) + }} + />
- )} -
- {withContext && ( -
- - - - - {upperCaseFirstLetter(String(current?.type))}:{' '} - {String(current?.name ?? 'No context')} - - - - + +
- )} - setInputMessage(e.target.value)} - onClick={handleSendMessage} - onKeyDown={(e) => { - if (e.key === 'Enter' && !e.shiftKey && !isLoading) { - e.preventDefault() - handleSendMessage() - } else { - if (inputRef.current) adjustTextareaHeight(inputRef.current) - } - }} - onInput={(e) => adjustTextareaHeight(e.target as HTMLTextAreaElement)} - loading={isLoading} - stop={() => { - setIsStopped(true) - setIsFinish(true) - }} - /> -
-
-
- {isReadOnly ? 'Read-only mode' : 'Read-write mode'} - - - + {appStatus && appStatus.status ? ( + + + {match(appStatus) + .with({ status: 'OPERATIONAL' }, () => 'All systems operational') + .with({ status: 'MAJOROUTAGE' }, () => 'Major outage ongoing') + .with({ status: 'MINOROUTAGE' }, () => 'Minor outage ongoing') + .with({ status: 'PARTIALOUTAGE' }, () => 'Partial outage ongoing') + .exhaustive()} + + 'green' as const) + .with({ status: 'MAJOROUTAGE' }, () => 'red' as const) + .with({ status: 'MINOROUTAGE' }, () => 'yellow' as const) + .with({ status: 'PARTIALOUTAGE' }, () => 'yellow' as const) + .exhaustive()} + /> + + ) : ( +
+ )}
- {appStatus && appStatus.status ? ( - - - {match(appStatus) - .with({ status: 'OPERATIONAL' }, () => 'All systems operational') - .with({ status: 'MAJOROUTAGE' }, () => 'Major outage ongoing') - .with({ status: 'MINOROUTAGE' }, () => 'Minor outage ongoing') - .with({ status: 'PARTIALOUTAGE' }, () => 'Partial outage ongoing') - .exhaustive()} - - 'green' as const) - .with({ status: 'MAJOROUTAGE' }, () => 'red' as const) - .with({ status: 'MINOROUTAGE' }, () => 'yellow' as const) - .with({ status: 'PARTIALOUTAGE' }, () => 'yellow' as const) - .exhaustive()} - /> - - ) : ( -
- )}
-
+ ) : ( +
+
+ + + + +
+
+ AI Copilot not activated yet + + Our DevOps AI Copilot can help you fix your deployments, optimize your infrastructure costs, audit + your security and do everything you would expect from a complete DevOps Engineering team. Enable it + in your organization settings to get started. + +
+ + + + +
+ )}
diff --git a/libs/shared/devops-copilot/feature/src/lib/devops-copilot-panel/header/header.tsx b/libs/shared/devops-copilot/feature/src/lib/devops-copilot-panel/header/header.tsx index 9e8d511fa17..1408ccf4e25 100644 --- a/libs/shared/devops-copilot/feature/src/lib/devops-copilot-panel/header/header.tsx +++ b/libs/shared/devops-copilot/feature/src/lib/devops-copilot-panel/header/header.tsx @@ -40,7 +40,7 @@ export function Header({ setPlan, }: HeaderProps) { return ( -
+
{!threadId || threads.length === 0 @@ -58,7 +58,7 @@ export function Header({ Beta - {userAccess?.read_only === false && threadLength == 0 && ( + {userAccess?.read_only === false && threadLength === 0 && ( <>