mirror of
https://github.com/hanzoai/sentinel.git
synced 2026-09-29 22:34:33 +00:00
159 lines
4.2 KiB
TypeScript
159 lines
4.2 KiB
TypeScript
import styled from '@emotion/styled';
|
|
import type {HTMLMotionProps, Variants} from 'framer-motion';
|
|
import {AnimatePresence, motion} from 'framer-motion';
|
|
|
|
import {Button} 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';
|
|
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<EventWaiterProps, 'children' | 'api'> {
|
|
children: (props: RenderProps) => React.ReactNode;
|
|
}
|
|
|
|
function FirstEventIndicator({children, ...props}: FirstEventIndicatorProps) {
|
|
return (
|
|
<EventWaiter {...props}>
|
|
{({firstIssue}) =>
|
|
children({
|
|
indicator: <Indicator firstIssue={firstIssue} {...props} />,
|
|
firstEventButton: (
|
|
<Button
|
|
title={t("You'll need to send your first error to continue")}
|
|
tooltipProps={{disabled: !!firstIssue}}
|
|
disabled={!firstIssue}
|
|
priority="primary"
|
|
onClick={() =>
|
|
trackAnalytics('growth.onboarding_take_to_error', {
|
|
organization: props.organization,
|
|
})
|
|
}
|
|
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')}
|
|
</Button>
|
|
),
|
|
})
|
|
}
|
|
</EventWaiter>
|
|
);
|
|
}
|
|
|
|
interface IndicatorProps extends Omit<EventWaiterProps, 'children' | 'api'> {
|
|
firstIssue: null | boolean | Group;
|
|
}
|
|
|
|
function Indicator({firstIssue}: IndicatorProps) {
|
|
return (
|
|
<Container>
|
|
<AnimatePresence>
|
|
{!firstIssue ? <Waiting key="waiting" /> : <Success key="received" />}
|
|
</AnimatePresence>
|
|
</Container>
|
|
);
|
|
}
|
|
|
|
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 (
|
|
<StatusWrapper {...props}>
|
|
<AnimatedText>{t('Waiting to receive first event to continue')}</AnimatedText>
|
|
<WaitingIndicator />
|
|
</StatusWrapper>
|
|
);
|
|
}
|
|
|
|
function Success(props: HTMLMotionProps<'div'>) {
|
|
return (
|
|
<StatusWrapper {...props}>
|
|
<AnimatedText>{t('Event was received!')}</AnimatedText>
|
|
<ReceivedIndicator />
|
|
</StatusWrapper>
|
|
);
|
|
}
|
|
|
|
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;
|