mirror of
https://github.com/BillyOutlast/posthog.git
synced 2026-02-04 03:01:23 +01:00
chore: make replay and error tracking the same style (#41421)
This commit is contained in:
@@ -12,7 +12,8 @@ export function LemonTableLink({
|
||||
title,
|
||||
description,
|
||||
...props
|
||||
}: Pick<LinkProps, 'to' | 'onClick' | 'target' | 'className'> & LemonTableLinkContentProps): JSX.Element {
|
||||
}: Pick<LinkProps, 'to' | 'onClick' | 'target' | 'className' | 'targetBlankIcon'> &
|
||||
LemonTableLinkContentProps): JSX.Element {
|
||||
if (!props.to) {
|
||||
return <LemonTableLinkContent title={title} description={description} />
|
||||
}
|
||||
|
||||
@@ -19,7 +19,10 @@ export interface RecordingRowProps {
|
||||
|
||||
type ACTIVITY_DESCRIPTIONS = 'very low' | 'low' | 'medium' | 'high' | 'very high'
|
||||
|
||||
function ActivityScoreLabel({ score }: { score: number | undefined }): JSX.Element {
|
||||
function getActivityScoreDescription({ score }: { score: number | undefined }): {
|
||||
description: ACTIVITY_DESCRIPTIONS
|
||||
backgroundColor: string
|
||||
} {
|
||||
const n = score ?? 0
|
||||
let backgroundColor = 'bg-primary-alt-highlight'
|
||||
let description: ACTIVITY_DESCRIPTIONS = 'very low'
|
||||
@@ -37,6 +40,22 @@ function ActivityScoreLabel({ score }: { score: number | undefined }): JSX.Eleme
|
||||
description = 'low'
|
||||
}
|
||||
|
||||
return { description, backgroundColor }
|
||||
}
|
||||
|
||||
export function ActivityScoreLabel({
|
||||
score,
|
||||
clean = false,
|
||||
}: {
|
||||
score: number | undefined
|
||||
clean?: boolean
|
||||
}): JSX.Element {
|
||||
const { description, backgroundColor } = getActivityScoreDescription({ score })
|
||||
|
||||
if (clean) {
|
||||
return <>{description}</>
|
||||
}
|
||||
|
||||
return <LemonSnack className={clsx(backgroundColor, 'text-xs')}>activity: {description}</LemonSnack>
|
||||
}
|
||||
|
||||
|
||||
@@ -24,26 +24,25 @@ export const CustomGroupTitleColumn: QueryContextColumnComponent = (props) => {
|
||||
return (
|
||||
<div className="flex items-start gap-x-1.5 group">
|
||||
<LemonTableLink
|
||||
target="_blank"
|
||||
title={record.name || 'Unknown Type'}
|
||||
description={
|
||||
<div className="deprecated-space-y-1">
|
||||
<div className="line-clamp-1">{record.description}</div>
|
||||
<div className="deprecated-space-x-1">
|
||||
<TZLabel time={record.last_seen} className="border-dotted border-b" />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
className="flex-1"
|
||||
description={<div className="line-clamp-1">{record.description}</div>}
|
||||
className="flex"
|
||||
to={urls.errorTrackingIssue(record.id)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const LastSeenColumn = ({ record }: { record: unknown }): JSX.Element => {
|
||||
const last_seen = (record as ErrorTrackingIssue).last_seen
|
||||
return <TZLabel time={last_seen} className="border-dotted border-b" />
|
||||
}
|
||||
|
||||
const CountColumn = ({ record, columnName }: { record: unknown; columnName: string }): JSX.Element => {
|
||||
const aggregations = (record as ErrorTrackingIssue).aggregations!
|
||||
const count = aggregations[columnName as 'occurrences' | 'users']
|
||||
return <span className="text-lg font-medium">{humanFriendlyLargeNumber(count)}</span>
|
||||
return <>{humanFriendlyLargeNumber(count)}</>
|
||||
}
|
||||
|
||||
const context: QueryContext = {
|
||||
@@ -52,7 +51,7 @@ const context: QueryContext = {
|
||||
showQueryEditor: false,
|
||||
columns: {
|
||||
error: {
|
||||
width: '50%',
|
||||
align: 'left',
|
||||
render: CustomGroupTitleColumn,
|
||||
},
|
||||
users: {
|
||||
@@ -63,6 +62,11 @@ const context: QueryContext = {
|
||||
align: 'right',
|
||||
render: CountColumn,
|
||||
},
|
||||
last_seen: {
|
||||
title: 'Last seen',
|
||||
align: 'right',
|
||||
render: LastSeenColumn,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -1,19 +1,25 @@
|
||||
import clsx from 'clsx'
|
||||
import { useActions, useValues } from 'kea'
|
||||
|
||||
import { IconRewindPlay } from '@posthog/icons'
|
||||
|
||||
import { EmptyMessage } from 'lib/components/EmptyMessage/EmptyMessage'
|
||||
import { LemonButton } from 'lib/lemon-ui/LemonButton'
|
||||
import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton'
|
||||
import { LemonTable } from 'lib/lemon-ui/LemonTable'
|
||||
import { ProfilePicture } from 'lib/lemon-ui/ProfilePicture'
|
||||
import { IconOpenInNew } from 'lib/lemon-ui/icons'
|
||||
import { humanFriendlyDuration } from 'lib/utils'
|
||||
import { ProductIntentContext } from 'lib/utils/product-intents'
|
||||
import { RecordingRow } from 'scenes/session-recordings/components/RecordingRow'
|
||||
import { asDisplay } from 'scenes/persons/person-utils'
|
||||
import { ActivityScoreLabel } from 'scenes/session-recordings/components/RecordingRow'
|
||||
import { sessionRecordingsPlaylistLogic } from 'scenes/session-recordings/playlist/sessionRecordingsPlaylistLogic'
|
||||
import { teamLogic } from 'scenes/teamLogic'
|
||||
import { urls } from 'scenes/urls'
|
||||
import { ReplayTile } from 'scenes/web-analytics/common'
|
||||
import { webAnalyticsLogic } from 'scenes/web-analytics/webAnalyticsLogic'
|
||||
|
||||
import { ProductKey, ReplayTabs } from '~/types'
|
||||
import { ProductKey, ReplayTabs, SessionRecordingType } from '~/types'
|
||||
|
||||
export function WebAnalyticsRecordingsTile({ tile }: { tile: ReplayTile }): JSX.Element {
|
||||
const { layout } = tile
|
||||
@@ -72,7 +78,50 @@ export function WebAnalyticsRecordingsTile({ tile }: { tile: ReplayTile }): JSX.
|
||||
) : items.length === 0 && emptyMessage ? (
|
||||
<EmptyMessage {...emptyMessage} />
|
||||
) : (
|
||||
items.map((item, index) => <RecordingRow key={index} recording={item} />)
|
||||
<LemonTable
|
||||
className="mt-4"
|
||||
columns={[
|
||||
{
|
||||
title: 'Person',
|
||||
render: (_, recording: SessionRecordingType) => (
|
||||
<>
|
||||
<ProfilePicture
|
||||
size="sm"
|
||||
name={asDisplay(recording.person)}
|
||||
className="mr-2"
|
||||
/>
|
||||
{asDisplay(recording.person)}{' '}
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Activity',
|
||||
render: (_, recording: SessionRecordingType) => (
|
||||
<>
|
||||
<ActivityScoreLabel score={recording.activity_score} clean={true} />
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Duration',
|
||||
render: (_, recording: SessionRecordingType) => (
|
||||
<>{humanFriendlyDuration(recording.recording_duration)}</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '',
|
||||
render: (_, recording: SessionRecordingType) => (
|
||||
<LemonButton
|
||||
size="xsmall"
|
||||
targetBlank
|
||||
to={urls.replaySingle(recording.id ?? '')}
|
||||
icon={<IconRewindPlay />}
|
||||
/>
|
||||
),
|
||||
},
|
||||
]}
|
||||
dataSource={items}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-row-reverse my-2">
|
||||
|
||||
@@ -1158,7 +1158,7 @@ export const webAnalyticsLogic = kea<webAnalyticsLogicType>([
|
||||
dateRange: dateRange,
|
||||
filterTestAccounts: filterTestAccounts,
|
||||
filterGroup: replayFilters.filter_group,
|
||||
columns: ['error', 'users', 'occurrences'],
|
||||
columns: ['error', 'users', 'occurrences', 'last_seen'],
|
||||
limit: 4,
|
||||
})
|
||||
} catch (e) {
|
||||
|
||||
Reference in New Issue
Block a user