import styled from '@emotion/styled'; import type {HTMLMotionProps, Variants} from 'framer-motion'; import {AnimatePresence, motion} from 'framer-motion'; import {LinkButton} from 'sentry/components/button'; import {IconCheckmark} from 'sentry/icons'; import {t} from 'sentry/locale'; import pulsingIndicatorStyles from 'sentry/styles/pulsingIndicator'; import {space} from 'sentry/styles/space'; import type {Group} from 'sentry/types/group'; import {trackAnalytics} from 'sentry/utils/analytics'; import type {EventWaiterProps} from 'sentry/utils/eventWaiter'; import EventWaiter from 'sentry/utils/eventWaiter'; import testableTransition from 'sentry/utils/testableTransition'; type RenderProps = { firstEventButton: React.ReactNode; indicator: React.ReactNode; }; interface FirstEventIndicatorProps extends Omit { children: (props: RenderProps) => React.ReactNode; } function FirstEventIndicator({children, ...props}: FirstEventIndicatorProps) { return ( {({firstIssue}) => children({ indicator: , firstEventButton: ( trackAnalytics('growth.onboarding_take_to_error', { organization: props.organization, platform: props.project.platform, }) } to={`/organizations/${props.organization.slug}/issues/${ firstIssue && firstIssue !== true && 'id' in firstIssue ? `${firstIssue.id}/` : '' }?referrer=onboarding-first-event-indicator`} > {t('Take me to my error')} ), }) } ); } interface IndicatorProps extends Omit { firstIssue: null | boolean | Group; } function Indicator({firstIssue}: IndicatorProps) { return ( {!firstIssue ? : } ); } const Container = styled('div')` display: grid; grid-template-columns: 1fr; justify-content: right; `; const StatusWrapper = styled(motion.div)` display: grid; grid-template-columns: 1fr max-content; gap: ${space(1)}; align-items: center; font-size: ${p => p.theme.fontSizeMedium}; /* Keep the wrapper in the parent grids first cell for transitions */ grid-column: 1; grid-row: 1; `; StatusWrapper.defaultProps = { initial: 'initial', animate: 'animate', exit: 'exit', variants: { initial: {opacity: 0, y: -10}, animate: { opacity: 1, y: 0, transition: testableTransition({when: 'beforeChildren', staggerChildren: 0.35}), }, exit: {opacity: 0, y: 10}, }, }; function Waiting(props: HTMLMotionProps<'div'>) { return ( {t('Waiting to receive first event to continue')} ); } function Success(props: HTMLMotionProps<'div'>) { return ( {t('Event was received!')} ); } const indicatorAnimation: Variants = { initial: {opacity: 0, y: -10}, animate: {opacity: 1, y: 0}, exit: {opacity: 0, y: 10}, }; const AnimatedText = styled(motion.div)``; AnimatedText.defaultProps = { variants: indicatorAnimation, transition: testableTransition(), }; const WaitingIndicator = styled(motion.div)` margin: 0 6px; ${pulsingIndicatorStyles}; `; WaitingIndicator.defaultProps = { variants: indicatorAnimation, transition: testableTransition(), }; const ReceivedIndicator = styled(IconCheckmark)` color: #fff; background: ${p => p.theme.green300}; border-radius: 50%; padding: 3px; margin: 0 ${space(0.25)}; `; ReceivedIndicator.defaultProps = { size: 'sm', }; export {Indicator}; export default FirstEventIndicator;