1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-02-14 00:19:16 +01:00

chore: add Unleash AI to New in Unleash (#8642)

https://linear.app/unleash/issue/2-2910/add-unleash-ai-to-new-in-unleash

Adds a new "Unleash AI" item to the "New in Unleash" section.

We don’t have documentation to link to yet, as this is still an
experimental feature. However, I’m considering adding a “Check it out”
button that highlights the button, which I can introduce in a separate
PR.


![image](https://github.com/user-attachments/assets/520362b2-627c-415e-b0bb-296825d5d8ee)
This commit is contained in:
Nuno Góis 2024-11-04 14:20:26 +00:00 committed by GitHub
parent 4a67607a0c
commit 2e99452645
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 140 additions and 65 deletions

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 44 KiB

View File

@ -1,6 +1,6 @@
import { mutate } from 'swr'; import { mutate } from 'swr';
import { ReactComponent as AIIcon } from 'assets/icons/AI.svg'; import { ReactComponent as AIIcon } from 'assets/icons/AI.svg';
import { IconButton, styled, useMediaQuery } from '@mui/material'; import { IconButton, styled, Tooltip, useMediaQuery } from '@mui/material';
import { useEffect, useRef, useState } from 'react'; import { useEffect, useRef, useState } from 'react';
import useToast from 'hooks/useToast'; import useToast from 'hooks/useToast';
import { formatUnknownError } from 'utils/formatUnknownError'; import { formatUnknownError } from 'utils/formatUnknownError';
@ -198,19 +198,21 @@ export const AIChat = () => {
if (!open) { if (!open) {
return ( return (
<StyledAIIconContainer demoStepsVisible={demoStepsVisible}> <StyledAIIconContainer demoStepsVisible={demoStepsVisible}>
<StyledAIIconButton <Tooltip arrow title='Unleash AI'>
size='large' <StyledAIIconButton
onClick={() => { size='large'
trackEvent('unleash-ai-chat', { onClick={() => {
props: { trackEvent('unleash-ai-chat', {
eventType: 'open', props: {
}, eventType: 'open',
}); },
setOpen(true); });
}} setOpen(true);
> }}
<AIIcon /> >
</StyledAIIconButton> <AIIcon />
</StyledAIIconButton>
</Tooltip>
</StyledAIIconContainer> </StyledAIIconContainer>
); );
} }

View File

@ -1,4 +1,3 @@
import type { ReactNode } from 'react';
import { useUiFlag } from 'hooks/useUiFlag'; import { useUiFlag } from 'hooks/useUiFlag';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import { useLocalStorageState } from 'hooks/useLocalStorageState'; import { useLocalStorageState } from 'hooks/useLocalStorageState';
@ -13,13 +12,18 @@ import {
} from '@mui/material'; } from '@mui/material';
import Signals from '@mui/icons-material/Sensors'; import Signals from '@mui/icons-material/Sensors';
import type { NavigationMode } from 'component/layout/MainLayout/NavigationSidebar/NavigationMode'; import type { NavigationMode } from 'component/layout/MainLayout/NavigationSidebar/NavigationMode';
import { NewInUnleashItem } from './NewInUnleashItem'; import {
NewInUnleashItem,
type NewInUnleashItemDetails,
} from './NewInUnleashItem';
import { usePlausibleTracker } from 'hooks/usePlausibleTracker'; import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
import { ReactComponent as SignalsPreview } from 'assets/img/signals.svg'; import { ReactComponent as SignalsPreview } from 'assets/img/signals.svg';
import LinearScaleIcon from '@mui/icons-material/LinearScale'; import LinearScaleIcon from '@mui/icons-material/LinearScale';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { useEventTimelineContext } from 'component/events/EventTimeline/EventTimelineContext'; import { useEventTimelineContext } from 'component/events/EventTimeline/EventTimelineContext';
import { ReactComponent as EventTimelinePreview } from 'assets/img/eventTimeline.svg'; import { ReactComponent as EventTimelinePreview } from 'assets/img/eventTimeline.svg';
import { ReactComponent as AIIcon } from 'assets/icons/AI.svg';
import { ReactComponent as AIPreview } from 'assets/img/aiPreview.svg';
const StyledNewInUnleash = styled('div')(({ theme }) => ({ const StyledNewInUnleash = styled('div')(({ theme }) => ({
margin: theme.spacing(2, 0, 1, 0), margin: theme.spacing(2, 0, 1, 0),
@ -75,17 +79,11 @@ const StyledLinearScaleIcon = styled(LinearScaleIcon)(({ theme }) => ({
color: theme.palette.primary.main, color: theme.palette.primary.main,
})); }));
type NewItem = { const StyledAIIcon = styled(AIIcon)(({ theme }) => ({
label: string; '& > path': {
summary: string; fill: theme.palette.primary.main,
icon: ReactNode; },
onCheckItOut: () => void; }));
docsLink: string;
show: boolean;
longDescription: ReactNode;
preview?: ReactNode;
beta?: boolean;
};
interface INewInUnleashProps { interface INewInUnleashProps {
mode?: NavigationMode; mode?: NavigationMode;
@ -102,12 +100,17 @@ export const NewInUnleash = ({
'new-in-unleash-seen:v1', 'new-in-unleash-seen:v1',
new Set(), new Set(),
); );
const { isOss, isEnterprise } = useUiConfig(); const {
isOss,
isEnterprise,
uiConfig: { unleashAIAvailable },
} = useUiConfig();
const signalsEnabled = useUiFlag('signals'); const signalsEnabled = useUiFlag('signals');
const unleashAIEnabled = useUiFlag('unleashAI');
const { setHighlighted } = useEventTimelineContext(); const { setHighlighted } = useEventTimelineContext();
const items: NewItem[] = [ const items: NewInUnleashItemDetails[] = [
{ {
label: 'Signals & Actions', label: 'Signals & Actions',
summary: 'Listen to signals via Webhooks', summary: 'Listen to signals via Webhooks',
@ -174,6 +177,30 @@ export const NewInUnleash = ({
</> </>
), ),
}, },
{
label: 'Unleash AI',
summary:
'Enhance your Unleash experience with the help of the Unleash AI assistant',
icon: <StyledAIIcon />,
preview: <AIPreview />,
show: Boolean(unleashAIAvailable) && unleashAIEnabled,
beta: true,
longDescription: (
<>
<p>
Meet the Unleash AI assistant, designed to make your
experience with Unleash easier and more intuitive,
whether you're handling tasks or looking for guidance.
</p>
<p>
Start chatting by using the button in the bottom right
corner of the page, and discover all the ways the
Unleash AI assistant can help you.
</p>
</>
),
},
]; ];
const visibleItems = items.filter( const visibleItems = items.filter(

View File

@ -13,6 +13,18 @@ import { NewInUnleashTooltip } from './NewInUnleashTooltip';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { Badge } from 'component/common/Badge/Badge'; import { Badge } from 'component/common/Badge/Badge';
export type NewInUnleashItemDetails = {
label: string;
summary: string;
icon: ReactNode;
onCheckItOut?: () => void;
docsLink?: string;
show: boolean;
longDescription: ReactNode;
preview?: ReactNode;
beta?: boolean;
};
const StyledItemButton = styled(ListItemButton)(({ theme }) => ({ const StyledItemButton = styled(ListItemButton)(({ theme }) => ({
outline: `1px solid ${theme.palette.divider}`, outline: `1px solid ${theme.palette.divider}`,
borderRadius: theme.shape.borderRadiusMedium, borderRadius: theme.shape.borderRadiusMedium,
@ -32,22 +44,17 @@ const StyledItemTitle = styled('div')(({ theme }) => ({
display: 'flex', display: 'flex',
gap: theme.spacing(1), gap: theme.spacing(1),
alignItems: 'center', alignItems: 'center',
height: theme.spacing(3),
})); }));
const StyledItemButtonClose = styled(IconButton)(({ theme }) => ({ const StyledItemButtonClose = styled(IconButton)(({ theme }) => ({
padding: theme.spacing(0.25), padding: theme.spacing(0.25),
})); }));
interface INewInUnleashItemProps { interface INewInUnleashItemProps
icon: ReactNode; extends Omit<NewInUnleashItemDetails, 'show' | 'beta'> {
onClick: () => void; onClick: () => void;
onDismiss: () => void; onDismiss: () => void;
label: string;
longDescription: ReactNode;
onCheckItOut: () => void;
docsLink: string;
preview?: ReactNode;
summary: string;
beta: boolean; beta: boolean;
} }

View File

@ -78,15 +78,20 @@ const StyledTitle = styled('div')(({ theme }) => ({
})); }));
const ReadMore = styled(Box)(({ theme }) => ({ const ReadMore = styled(Box)(({ theme }) => ({
padding: theme.spacing(3, 0), paddingTop: theme.spacing(3),
paddingBottom: theme.spacing(1),
}));
const StyledCheckItOutButton = styled(Button)(({ theme }) => ({
marginTop: theme.spacing(2),
})); }));
export const NewInUnleashTooltip: FC<{ export const NewInUnleashTooltip: FC<{
children: React.ReactElement<any, any>; children: React.ReactElement<any, any>;
title: string; title: string;
longDescription: ReactNode; longDescription: ReactNode;
docsLink: string; docsLink?: string;
onCheckItOut: () => void; onCheckItOut?: () => void;
open: boolean; open: boolean;
preview?: ReactNode; preview?: ReactNode;
onClose: () => void; onClose: () => void;
@ -134,31 +139,41 @@ export const NewInUnleashTooltip: FC<{
/> />
</StyledTitle> </StyledTitle>
<LongDescription>{longDescription}</LongDescription> <LongDescription>{longDescription}</LongDescription>
<ReadMore> <ConditionallyRender
<StyledLink condition={Boolean(docsLink)}
component='a' show={
href={docsLink} <ReadMore>
underline='hover' <StyledLink
rel='noopener noreferrer' component='a'
target='_blank' href={docsLink}
> underline='hover'
<StyledOpenInNew /> Read more in our rel='noopener noreferrer'
documentation target='_blank'
</StyledLink> >
</ReadMore> <StyledOpenInNew /> Read more in our
<Button documentation
variant='contained' </StyledLink>
color='primary' </ReadMore>
type='submit' }
size='small' />
onClick={(event) => { <ConditionallyRender
event.stopPropagation(); condition={Boolean(onCheckItOut)}
onClose(); show={
onCheckItOut(); <StyledCheckItOutButton
}} variant='contained'
> color='primary'
Check it out type='submit'
</Button> size='small'
onClick={(event) => {
event.stopPropagation();
onClose();
onCheckItOut!();
}}
>
Check it out
</StyledCheckItOutButton>
}
/>
</Body> </Body>
</Box> </Box>
</ClickAwayListener> </ClickAwayListener>