|
@@ -1,4 +1,4 @@
|
|
|
-import {useState} from 'react';
|
|
|
+import {Fragment} from 'react';
|
|
|
import styled from '@emotion/styled';
|
|
|
|
|
|
import bannerBackground from 'sentry-images/spot/ai-suggestion-banner-background.svg';
|
|
@@ -6,20 +6,12 @@ import bannerSentaur from 'sentry-images/spot/ai-suggestion-banner-sentaur.svg';
|
|
|
import bannerStars from 'sentry-images/spot/ai-suggestion-banner-stars.svg';
|
|
|
|
|
|
import {Button} from 'sentry/components/button';
|
|
|
-import ButtonBar from 'sentry/components/buttonBar';
|
|
|
-import EmptyMessage from 'sentry/components/emptyMessage';
|
|
|
-import TextArea from 'sentry/components/forms/controls/textarea';
|
|
|
-import ExternalLink from 'sentry/components/links/externalLink';
|
|
|
+import Input from 'sentry/components/input';
|
|
|
import Panel from 'sentry/components/panels/panel';
|
|
|
import PanelBody from 'sentry/components/panels/panelBody';
|
|
|
-import QuestionTooltip from 'sentry/components/questionTooltip';
|
|
|
-import {IconFlag} from 'sentry/icons';
|
|
|
-import {t, tct} from 'sentry/locale';
|
|
|
+import {t} from 'sentry/locale';
|
|
|
import {space} from 'sentry/styles/space';
|
|
|
import {useIsSentryEmployee} from 'sentry/utils/useIsSentryEmployee';
|
|
|
-import TextBlock from 'sentry/views/settings/components/text/textBlock';
|
|
|
-
|
|
|
-import {ExperimentalFeatureBadge} from '../aiSuggestedSolution/experimentalFeatureBadge';
|
|
|
|
|
|
type Props = {
|
|
|
additionalContext: string;
|
|
@@ -29,78 +21,45 @@ type Props = {
|
|
|
|
|
|
export function Banner({onButtonClick, additionalContext, setAdditionalContext}: Props) {
|
|
|
const isSentryEmployee = useIsSentryEmployee();
|
|
|
- const [piiCertified, setPiiCertified] = useState(false);
|
|
|
-
|
|
|
- const showPiiMessage = isSentryEmployee && !piiCertified;
|
|
|
|
|
|
return (
|
|
|
<Wrapper>
|
|
|
+ <IllustrationContainer>
|
|
|
+ <Background src={bannerBackground} />
|
|
|
+ <Stars src={bannerStars} />
|
|
|
+ <Sentaur src={bannerSentaur} />
|
|
|
+ </IllustrationContainer>
|
|
|
<Body>
|
|
|
- <Header>
|
|
|
- <TitleContent>
|
|
|
- <Title>
|
|
|
- {t('AI Autofix')}
|
|
|
- <MoreInfoTooltip
|
|
|
- isHoverable
|
|
|
- size="sm"
|
|
|
- title={tct(
|
|
|
- 'This is an OpenAI generated solution that automatically creates a pull request for this issue. Be aware that this may not be accurate. [learnMore:Learn more]',
|
|
|
- {
|
|
|
- learnMore: (
|
|
|
- <ExternalLink href="https://docs.sentry.io/product/issues/issue-details/ai-suggested-solution/" />
|
|
|
- ),
|
|
|
- }
|
|
|
- )}
|
|
|
- />
|
|
|
- <ExperimentalFeatureBadge />
|
|
|
- </Title>
|
|
|
- <Description>
|
|
|
- {t('You might get lucky, but again, maybe not\u2026')}
|
|
|
- </Description>
|
|
|
- </TitleContent>
|
|
|
- <Action>
|
|
|
- <Background src={bannerBackground} />
|
|
|
- <Stars src={bannerStars} />
|
|
|
- <Sentaur src={bannerSentaur} />
|
|
|
- <ViewSuggestionButton
|
|
|
- size="xs"
|
|
|
- onClick={onButtonClick}
|
|
|
- disabled={showPiiMessage}
|
|
|
- >
|
|
|
- {t('Try your luck')}
|
|
|
- </ViewSuggestionButton>
|
|
|
- </Action>
|
|
|
- </Header>
|
|
|
- {showPiiMessage ? (
|
|
|
- <PiiArea>
|
|
|
- <EmptyMessage
|
|
|
- icon={<IconFlag size="xl" />}
|
|
|
- title={t('PII Certification Required')}
|
|
|
- description={t(
|
|
|
- 'Before using this feature, please confirm that there is no personally identifiable information in this event.'
|
|
|
- )}
|
|
|
- action={
|
|
|
- <ButtonBar gap={2}>
|
|
|
- <Button priority="primary" onClick={() => setPiiCertified(true)}>
|
|
|
- {t('Certify No PII')}
|
|
|
- </Button>
|
|
|
- </ButtonBar>
|
|
|
+ <div>
|
|
|
+ <Title>{t('Try AI Autofix')}</Title>
|
|
|
+ <SubTitle>{t('You might get lucky, but then again, maybe not...')}</SubTitle>
|
|
|
+ </div>
|
|
|
+ <ContextArea>
|
|
|
+ <Button onClick={onButtonClick} size="sm">
|
|
|
+ {t('Gimme Fix')}
|
|
|
+ </Button>
|
|
|
+ <Input
|
|
|
+ aria-label={t('Provide additional context (optional)')}
|
|
|
+ value={additionalContext}
|
|
|
+ placeholder={t('Add context (optional)...')}
|
|
|
+ onChange={e => setAdditionalContext(e.target.value)}
|
|
|
+ onKeyDown={e => {
|
|
|
+ if (e.key === 'Enter') {
|
|
|
+ onButtonClick();
|
|
|
}
|
|
|
- />
|
|
|
- </PiiArea>
|
|
|
- ) : (
|
|
|
- <AdditionalContextArea>
|
|
|
- <AdditionalContextLabel>
|
|
|
- {t('Additional Context (Optional)')}
|
|
|
- </AdditionalContextLabel>
|
|
|
- <StyledTextArea
|
|
|
- value={additionalContext}
|
|
|
- placeholder={t(
|
|
|
- 'Add additional context to help the AI find a better solution'
|
|
|
+ }}
|
|
|
+ size="sm"
|
|
|
+ />
|
|
|
+ </ContextArea>
|
|
|
+ {isSentryEmployee && (
|
|
|
+ <Fragment>
|
|
|
+ <Separator />
|
|
|
+ <PiiMessage>
|
|
|
+ {t(
|
|
|
+ 'By clicking the button above, you confirm that there is no PII in this event.'
|
|
|
)}
|
|
|
- onChange={e => setAdditionalContext(e.target.value)}
|
|
|
- />
|
|
|
- </AdditionalContextArea>
|
|
|
+ </PiiMessage>
|
|
|
+ </Fragment>
|
|
|
)}
|
|
|
</Body>
|
|
|
</Wrapper>
|
|
@@ -109,133 +68,80 @@ export function Banner({onButtonClick, additionalContext, setAdditionalContext}:
|
|
|
|
|
|
const Wrapper = styled(Panel)`
|
|
|
margin-bottom: 0;
|
|
|
+ background: linear-gradient(269.35deg, ${p => p.theme.backgroundTertiary} 0.32%, rgba(245, 243, 247, 0) 99.69%);
|
|
|
`;
|
|
|
|
|
|
const Body = styled(PanelBody)`
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
-`;
|
|
|
-
|
|
|
-const Header = styled('div')`
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- flex-wrap: wrap;
|
|
|
- gap: ${space(1)};
|
|
|
-
|
|
|
- > *:first-child {
|
|
|
- flex: 1;
|
|
|
- }
|
|
|
+ padding: ${space(2)} ${space(3)};
|
|
|
|
|
|
@media (min-width: ${p => p.theme.breakpoints.xlarge}) {
|
|
|
- display: grid;
|
|
|
- grid-template-columns: 42% 1fr;
|
|
|
- height: 80px;
|
|
|
- position: relative;
|
|
|
+ max-width: calc(100% - 400px);
|
|
|
}
|
|
|
`;
|
|
|
|
|
|
const Title = styled('div')`
|
|
|
- font-size: ${p => p.theme.fontSizeSmall};
|
|
|
- text-transform: uppercase;
|
|
|
- color: ${p => p.theme.gray300};
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- /* to be consistent with the feature badge size */
|
|
|
- height: ${space(2)};
|
|
|
- line-height: ${space(2)};
|
|
|
- white-space: nowrap;
|
|
|
-`;
|
|
|
-
|
|
|
-const TitleContent = styled('div')`
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- padding-left: ${space(2)};
|
|
|
+ font-size: ${p => p.theme.fontSizeExtraLarge};
|
|
|
+ font-weight: bold;
|
|
|
+ margin-bottom: ${space(1)};
|
|
|
`;
|
|
|
|
|
|
-const Description = styled(TextBlock)`
|
|
|
- margin: ${space(1)} 0 0 0;
|
|
|
+const SubTitle = styled('p')`
|
|
|
+ margin: ${space(1)} 0;
|
|
|
`;
|
|
|
|
|
|
-const Action = styled('div')`
|
|
|
- display: flex;
|
|
|
- justify-content: flex-end;
|
|
|
- align-items: center;
|
|
|
+const ContextArea = styled('div')`
|
|
|
+ display: grid;
|
|
|
+ gap: ${space(1)};
|
|
|
+ grid-template-columns: auto 1fr;
|
|
|
+ margin-top: ${space(1)};
|
|
|
`;
|
|
|
|
|
|
-const Sentaur = styled('img')`
|
|
|
+const IllustrationContainer = styled('div')`
|
|
|
display: none;
|
|
|
- @media (min-width: ${p => p.theme.breakpoints.xlarge}) {
|
|
|
- display: block;
|
|
|
- height: 8.563rem;
|
|
|
- position: absolute;
|
|
|
- bottom: 0;
|
|
|
- right: 6.608rem;
|
|
|
- object-fit: cover;
|
|
|
- z-index: 1;
|
|
|
- pointer-events: none;
|
|
|
- }
|
|
|
-`;
|
|
|
|
|
|
-const Background = styled('img')`
|
|
|
- display: none;
|
|
|
@media (min-width: ${p => p.theme.breakpoints.xlarge}) {
|
|
|
display: block;
|
|
|
position: absolute;
|
|
|
- top: 0;
|
|
|
+ bottom: 0;
|
|
|
right: 0;
|
|
|
- object-fit: cover;
|
|
|
- max-width: 100%;
|
|
|
- height: 100%;
|
|
|
- border-top-right-radius: ${p => p.theme.panelBorderRadius};
|
|
|
+ top: 0;
|
|
|
+ width: 400px;
|
|
|
+ overflow: hidden;
|
|
|
+ border-radius: 0 ${p => p.theme.borderRadius} ${p => p.theme.borderRadius} 0;
|
|
|
}
|
|
|
`;
|
|
|
|
|
|
-const Stars = styled('img')`
|
|
|
- display: none;
|
|
|
- @media (min-width: ${p => p.theme.breakpoints.xlarge}) {
|
|
|
- display: block;
|
|
|
- height: 8.563rem;
|
|
|
- position: absolute;
|
|
|
- right: -1rem;
|
|
|
- bottom: -0.125rem;
|
|
|
- object-fit: cover;
|
|
|
- /* workaround to remove a extra svg on the bottom right */
|
|
|
- border-radius: ${p => p.theme.panelBorderRadius};
|
|
|
- }
|
|
|
+const Sentaur = styled('img')`
|
|
|
+ height: 125px;
|
|
|
+ position: absolute;
|
|
|
+ bottom: 0;
|
|
|
+ right: 185px;
|
|
|
+ z-index: 1;
|
|
|
+ pointer-events: none;
|
|
|
`;
|
|
|
|
|
|
-const ViewSuggestionButton = styled(Button)`
|
|
|
- @media (min-width: ${p => p.theme.breakpoints.xlarge}) {
|
|
|
- position: absolute;
|
|
|
- right: 1rem;
|
|
|
- top: 1.5rem;
|
|
|
- }
|
|
|
+const Background = styled('img')`
|
|
|
+ position: absolute;
|
|
|
+ bottom: 0;
|
|
|
+ right: 0;
|
|
|
+ max-width: 100%;
|
|
|
`;
|
|
|
|
|
|
-const MoreInfoTooltip = styled(QuestionTooltip)`
|
|
|
- margin-left: ${space(0.5)};
|
|
|
+const Stars = styled('img')`
|
|
|
+ pointer-events: none;
|
|
|
+ position: absolute;
|
|
|
+ right: -140px;
|
|
|
+ bottom: 40px;
|
|
|
+ height: 120px;
|
|
|
`;
|
|
|
|
|
|
-const AdditionalContextLabel = styled('label')`
|
|
|
- font-size: ${p => p.theme.fontSizeSmall};
|
|
|
- color: ${p => p.theme.gray300};
|
|
|
+const Separator = styled('hr')`
|
|
|
+ margin-top: ${space(2)};
|
|
|
margin-bottom: ${space(1)};
|
|
|
+ border-color: ${p => p.theme.translucentBorder};
|
|
|
`;
|
|
|
|
|
|
-const PiiArea = styled('div')`
|
|
|
- background-color: ${p => p.theme.backgroundSecondary};
|
|
|
- border-radius: ${p => p.theme.panelBorderRadius};
|
|
|
-`;
|
|
|
-
|
|
|
-const AdditionalContextArea = styled('div')`
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- padding: ${space(2)};
|
|
|
- background-color: ${p => p.theme.backgroundSecondary};
|
|
|
- border-radius: ${p => p.theme.panelBorderRadius};
|
|
|
-`;
|
|
|
-
|
|
|
-const StyledTextArea = styled(TextArea)`
|
|
|
- resize: vertical;
|
|
|
- min-height: 80px;
|
|
|
+const PiiMessage = styled('p')`
|
|
|
+ font-size: ${p => p.theme.fontSizeSmall};
|
|
|
+ color: ${p => p.theme.subText};
|
|
|
`;
|