From ababe9fe75e51972bdce0d91148395c03362b8a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20G=C3=B3is?= Date: Wed, 23 Oct 2024 15:59:02 +0100 Subject: [PATCH] chore: Unleash AI UX adjustments: placement, icon, color (#8521) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://linear.app/unleash/issue/2-2870/ux-adjustments-following-the-breakathon-placement-on-demo-bot-icon-and Post-breakathon UX adjustments, including: - Properly positions the Unleash AI chat option to the left of the demo steps, when the demo steps are visible. - Replaces the bot icon with a friendlier, more upbeat version. - Switches the chat purple color in the light theme to `primary.main` for better accessibility. Additionally, I’ve added the mode property to our themes for easier future maintenance. This makes it simple to check the currently active theme. ![image](https://github.com/user-attachments/assets/bc0c2f99-5460-4bc7-8aa0-e8d94156b669) --- frontend/src/assets/icons/AI.svg | 3 ++ frontend/src/component/ai/AIChat.tsx | 42 ++++++++++++++++----- frontend/src/component/ai/AIChatHeader.tsx | 9 +++-- frontend/src/component/ai/AIChatMessage.tsx | 9 +++-- frontend/src/themes/dark-theme.ts | 3 +- frontend/src/themes/theme.ts | 3 +- frontend/src/themes/themeTypes.ts | 1 + 7 files changed, 52 insertions(+), 18 deletions(-) create mode 100644 frontend/src/assets/icons/AI.svg diff --git a/frontend/src/assets/icons/AI.svg b/frontend/src/assets/icons/AI.svg new file mode 100644 index 000000000000..b6d25d059fb6 --- /dev/null +++ b/frontend/src/assets/icons/AI.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/component/ai/AIChat.tsx b/frontend/src/component/ai/AIChat.tsx index 703e9ff4bc7d..92b404291920 100644 --- a/frontend/src/component/ai/AIChat.tsx +++ b/frontend/src/component/ai/AIChat.tsx @@ -1,6 +1,6 @@ import { mutate } from 'swr'; -import SmartToyIcon from '@mui/icons-material/SmartToy'; -import { IconButton, styled } from '@mui/material'; +import { ReactComponent as AIIcon } from 'assets/icons/AI.svg'; +import { IconButton, styled, useMediaQuery } from '@mui/material'; import { useEffect, useRef, useState } from 'react'; import useToast from 'hooks/useToast'; import { formatUnknownError } from 'utils/formatUnknownError'; @@ -16,6 +16,7 @@ import { AIChatHeader } from './AIChatHeader'; import { Resizable } from 'component/common/Resizable/Resizable'; import { AIChatDisclaimer } from './AIChatDisclaimer'; import { usePlausibleTracker } from 'hooks/usePlausibleTracker'; +import theme from 'themes/theme'; const AI_ERROR_MESSAGE = { role: 'assistant', @@ -26,10 +27,15 @@ type ScrollOptions = ScrollIntoViewOptions & { onlyIfAtEnd?: boolean; }; -const StyledAIIconContainer = styled('div')(({ theme }) => ({ +const StyledAIIconContainer = styled('div', { + shouldForwardProp: (prop) => prop !== 'demoStepsVisible', +})<{ demoStepsVisible: boolean }>(({ theme, demoStepsVisible }) => ({ position: 'fixed', bottom: 20, right: 20, + ...(demoStepsVisible && { + right: 260, + }), zIndex: theme.zIndex.fab, animation: 'fadeInBottom 0.5s', '@keyframes fadeInBottom': { @@ -44,10 +50,15 @@ const StyledAIIconContainer = styled('div')(({ theme }) => ({ }, })); -const StyledAIChatContainer = styled(StyledAIIconContainer)({ +const StyledAIChatContainer = styled(StyledAIIconContainer, { + shouldForwardProp: (prop) => prop !== 'demoStepsVisible', +})<{ demoStepsVisible: boolean }>(({ demoStepsVisible }) => ({ bottom: 10, right: 10, -}); + ...(demoStepsVisible && { + right: 250, + }), +})); const StyledResizable = styled(Resizable)(({ theme }) => ({ boxShadow: theme.boxShadows.popup, @@ -55,13 +66,20 @@ const StyledResizable = styled(Resizable)(({ theme }) => ({ })); const StyledAIIconButton = styled(IconButton)(({ theme }) => ({ - background: theme.palette.primary.light, + background: + theme.mode === 'light' + ? theme.palette.primary.main + : theme.palette.primary.light, color: theme.palette.primary.contrastText, boxShadow: theme.boxShadows.popup, transition: 'background 0.3s', '&:hover': { background: theme.palette.primary.dark, }, + '& > svg': { + width: theme.spacing(3), + height: theme.spacing(3), + }, })); const StyledChat = styled('div')(({ theme }) => ({ @@ -84,6 +102,8 @@ const StyledChatContent = styled('div')(({ theme }) => ({ export const AIChat = () => { const unleashAIEnabled = useUiFlag('unleashAI'); + const demoEnabled = useUiFlag('demo'); + const isSmallScreen = useMediaQuery(theme.breakpoints.down(768)); const { uiConfig: { unleashAIAvailable }, } = useUiConfig(); @@ -169,13 +189,15 @@ export const AIChat = () => { newChat(); }; + const demoStepsVisible = demoEnabled && !isSmallScreen; + if (!unleashAIEnabled || !unleashAIAvailable) { return null; } if (!open) { return ( - + { @@ -187,18 +209,18 @@ export const AIChat = () => { setOpen(true); }} > - + ); } return ( - + scrollToEnd({ onlyIfAtEnd: true })} > diff --git a/frontend/src/component/ai/AIChatHeader.tsx b/frontend/src/component/ai/AIChatHeader.tsx index 4c5ff467022f..6087327c0375 100644 --- a/frontend/src/component/ai/AIChatHeader.tsx +++ b/frontend/src/component/ai/AIChatHeader.tsx @@ -1,10 +1,13 @@ import { IconButton, styled, Tooltip, Typography } from '@mui/material'; -import SmartToyIcon from '@mui/icons-material/SmartToy'; +import { ReactComponent as AIIcon } from 'assets/icons/AI.svg'; import EditNoteIcon from '@mui/icons-material/EditNote'; import CloseIcon from '@mui/icons-material/Close'; const StyledHeader = styled('div')(({ theme }) => ({ - background: theme.palette.primary.light, + background: + theme.mode === 'light' + ? theme.palette.primary.main + : theme.palette.primary.light, color: theme.palette.primary.contrastText, display: 'flex', alignItems: 'center', @@ -41,7 +44,7 @@ export const AIChatHeader = ({ onNew, onClose }: IAIChatHeaderProps) => { return ( - + Unleash AI diff --git a/frontend/src/component/ai/AIChatMessage.tsx b/frontend/src/component/ai/AIChatMessage.tsx index 868e6fedc150..6680d2c5a069 100644 --- a/frontend/src/component/ai/AIChatMessage.tsx +++ b/frontend/src/component/ai/AIChatMessage.tsx @@ -1,5 +1,5 @@ import { Avatar, styled } from '@mui/material'; -import SmartToyIcon from '@mui/icons-material/SmartToy'; +import { ReactComponent as AIIcon } from 'assets/icons/AI.svg'; import { Markdown } from 'component/common/Markdown/Markdown'; import { useAuthUser } from 'hooks/api/getters/useAuth/useAuthUser'; import type { ChatMessage } from 'hooks/api/actions/useAIApi/useAIApi'; @@ -62,7 +62,10 @@ const StyledUserMessage = styled(StyledAIMessage)(({ theme }) => ({ const StyledAvatar = styled(Avatar)(({ theme }) => ({ width: theme.spacing(4.5), height: theme.spacing(4.5), - backgroundColor: theme.palette.primary.light, + backgroundColor: + theme.mode === 'light' + ? theme.palette.primary.main + : theme.palette.primary.light, color: theme.palette.primary.contrastText, })); @@ -89,7 +92,7 @@ export const AIChatMessage = ({ from, children }: IAIChatMessageProps) => { return ( - + {children} diff --git a/frontend/src/themes/dark-theme.ts b/frontend/src/themes/dark-theme.ts index 45f0db4a0593..7dc17ce5892a 100644 --- a/frontend/src/themes/dark-theme.ts +++ b/frontend/src/themes/dark-theme.ts @@ -12,6 +12,7 @@ const actionColors = { }; const theme = { + mode: 'dark', breakpoints: { values: { xs: 0, @@ -309,7 +310,7 @@ const theme = { series: colors.chartSeries, }, }, -}; +} as const; export default createTheme({ ...theme, diff --git a/frontend/src/themes/theme.ts b/frontend/src/themes/theme.ts index c1ec1b52f726..98991269a531 100644 --- a/frontend/src/themes/theme.ts +++ b/frontend/src/themes/theme.ts @@ -4,6 +4,7 @@ import { alpha } from '@mui/material'; import { focusable } from 'themes/themeStyles'; export const theme = { + mode: 'light', breakpoints: { values: { xs: 0, @@ -294,7 +295,7 @@ export const theme = { series: colors.chartSeries, }, }, -}; +} as const; export default createTheme({ ...theme, diff --git a/frontend/src/themes/themeTypes.ts b/frontend/src/themes/themeTypes.ts index 79e59a06a694..7c3e053e6862 100644 --- a/frontend/src/themes/themeTypes.ts +++ b/frontend/src/themes/themeTypes.ts @@ -3,6 +3,7 @@ import { FormHelperTextOwnProps } from '@mui/material/FormHelperText'; declare module '@mui/material/styles' { interface CustomTheme { + mode: 'light' | 'dark'; /** * @deprecated */