diff --git a/static/app/components/timeline/index.tsx b/static/app/components/timeline/index.tsx index 5f303b01ad67d3..770e4e370f82a0 100644 --- a/static/app/components/timeline/index.tsx +++ b/static/app/components/timeline/index.tsx @@ -2,12 +2,13 @@ import type {CSSProperties} from 'react'; import {useTheme, type Theme} from '@emotion/react'; import styled from '@emotion/styled'; +import {Flex} from '@sentry/scraps/layout'; + import {space} from 'sentry/styles/space'; import type {Color} from 'sentry/utils/theme'; import {isChonkTheme} from 'sentry/utils/theme/withChonk'; export interface TimelineItemProps { - icon: React.ReactNode; title: React.ReactNode; children?: React.ReactNode; className?: string; @@ -16,6 +17,7 @@ export interface TimelineItemProps { iconBorder: string | Color; title: string | Color; }; + icon?: React.ReactNode; isActive?: boolean; onClick?: React.MouseEventHandler; onMouseEnter?: React.MouseEventHandler; @@ -24,6 +26,7 @@ export interface TimelineItemProps { showLastLine?: boolean; style?: CSSProperties; timestamp?: React.ReactNode; + titleTrailingItems?: React.ReactNode; } function Item({ @@ -33,6 +36,7 @@ function Item({ colorConfig, timestamp, isActive = false, + titleTrailingItems, ref, ...props }: TimelineItemProps) { @@ -45,16 +49,23 @@ function Item({ return ( - - {icon} - - {title} + {icon ? ( + + {icon} + + ) : ( + + )} + + {title} + {titleTrailingItems} + {timestamp ??
} {children} diff --git a/static/gsApp/views/subscriptionPage/usageLog.tsx b/static/gsApp/views/subscriptionPage/usageLog.tsx index 513c7cf8517a7e..e39c5b135eae5d 100644 --- a/static/gsApp/views/subscriptionPage/usageLog.tsx +++ b/static/gsApp/views/subscriptionPage/usageLog.tsx @@ -1,23 +1,25 @@ import {Fragment} from 'react'; -import styled from '@emotion/styled'; +import {useTheme} from '@emotion/react'; import type {Location} from 'history'; import upperFirst from 'lodash/upperFirst'; -import {ActivityAvatar} from 'sentry/components/activity/item/avatar'; -import {UserAvatar} from 'sentry/components/core/avatar/userAvatar'; +import {Container, Flex, Grid} from '@sentry/scraps/layout'; +import {Text} from '@sentry/scraps/text'; + import {Tag} from 'sentry/components/core/badge/tag'; import {CompactSelect} from 'sentry/components/core/compactSelect'; import {DateTime} from 'sentry/components/dateTime'; import LoadingError from 'sentry/components/loadingError'; import type {CursorHandler} from 'sentry/components/pagination'; import Pagination from 'sentry/components/pagination'; -import {PanelTable} from 'sentry/components/panels/panelTable'; +import Placeholder from 'sentry/components/placeholder'; import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle'; +import {Timeline} from 'sentry/components/timeline'; +import {IconCircleFill} from 'sentry/icons'; import {t} from 'sentry/locale'; -import {space} from 'sentry/styles/space'; import type {AuditLog} from 'sentry/types/organization'; import type {User} from 'sentry/types/user'; -import {shouldUse24Hours} from 'sentry/utils/dates'; +import {getTimeFormat} from 'sentry/utils/dates'; import {useApiQuery} from 'sentry/utils/queryClient'; import {decodeScalar} from 'sentry/utils/queryString'; import {useMemoWithPrevious} from 'sentry/utils/useMemoWithPrevious'; @@ -33,38 +35,24 @@ import SubscriptionPageContainer from 'getsentry/views/subscriptionPage/componen import SubscriptionHeader from './subscriptionHeader'; -const avatarStyle = { - width: 36, - height: 36, - marginRight: space(1), -}; - -function LogAvatar({logEntryUser}: {logEntryUser: User | undefined}) { - // Display Sentry's avatar for system or superuser-initiated events - if ( - logEntryUser?.isSuperuser || - (logEntryUser?.name === 'Sentry' && logEntryUser?.email === undefined) - ) { - return ; - } - // Display user's avatar for non-superusers-initiated events - if (logEntryUser !== undefined) { - return ; - } - return null; -} - function LogUsername({logEntryUser}: {logEntryUser: User | undefined}) { if (logEntryUser?.isSuperuser) { return ( - - {logEntryUser.name} + + + {logEntryUser.name} + {t('Sentry Staff')} - + ); } + if (logEntryUser?.name !== 'Sentry' && logEntryUser !== undefined) { - return {logEntryUser.name}; + return ( + + {logEntryUser.name} + + ); } return null; } @@ -95,9 +83,21 @@ type Props = { subscription: Subscription; }; +function SkeletonEntry() { + return ( + } + icon={} + > + + + ); +} + function UsageLog({location, subscription}: Props) { const organization = useOrganization(); const navigate = useNavigate(); + const theme = useTheme(); const { data: auditLogs, isPending, @@ -117,7 +117,6 @@ function UsageLog({location, subscription}: Props) { {staleTime: 0} ); - // const eventNames = useMemoWithPrevious( previous => auditLogs?.eventNames ?? previous, [auditLogs?.eventNames] @@ -160,7 +159,7 @@ function UsageLog({location, subscription}: Props) { const usageLogContent = ( - + {isError ? ( + ) : auditLogs?.rows?.length === 0 ? ( + {t('No entries available.')} ) : ( - - {auditLogs?.rows.map(entry => ( - - -
- -
- - - {formatEntryTitle(entry.event)} - {formatEntryMessage(entry.note)} - -
- - - - - -
- ))} -
+ + {isPending + ? Array.from({length: 50}).map((_, index) => ) + : auditLogs?.rows.map((entry, index) => ( + } + title={formatEntryTitle(entry.event)} + titleTrailingItems={ + + + {' ・ '} + + + + + {entry.actor && entry.actor.name !== 'Sentry' && ( + + + {' ・ '} + + + + )} + + } + > + + + {formatEntryMessage(entry.note)} + + + + ))} + )} -
+
); @@ -235,52 +250,3 @@ function UsageLog({location, subscription}: Props) { export default withSubscription(UsageLog); export {UsageLog}; - -const SentryAvatar = styled(ActivityAvatar)` - margin-right: ${space(1)}; -`; - -const Note = styled('div')` - font-size: ${p => p.theme.fontSize.md}; - word-break: break-word; -`; - -const StaffNote = styled(Note)` - display: flex; - gap: ${space(1)}; - line-height: 1.5; -`; - -const UsageLogContainer = styled('div')` - display: grid; - grid-auto-flow: row; - gap: ${space(3)}; -`; - -const UsageTable = styled(PanelTable)` - box-shadow: inset 0px -1px 0px ${p => p.theme.gray200}; -`; - -const UserInfo = styled('div')` - font-size: ${p => p.theme.fontSize.sm}; - min-width: 250px; - display: flex; -`; - -const NoteContainer = styled('div')` - display: flex; - flex-direction: column; - justify-content: center; -`; - -const Title = styled('div')` - font-size: ${p => p.theme.fontSize.lg}; -`; - -const TimestampInfo = styled('div')` - display: grid; - grid-template-columns: max-content auto; - gap: ${space(1)}; - font-size: ${p => p.theme.fontSize.md}; - align-content: center; -`;