Skip to content

Commit

Permalink
feat: ordered playlist results (#20493)
Browse files Browse the repository at this point in the history
  • Loading branch information
daibhin authored Feb 29, 2024
1 parent 47cf62b commit dbb3c09
Show file tree
Hide file tree
Showing 16 changed files with 299 additions and 69 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions frontend/src/lib/constants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ export const FEATURE_FLAGS = {
SAVED_NOT_PINNED: 'saved-not-pinned', // owner: #team-replay
EXPORTS_SIDEPANEL: 'exports-sidepanel', // owner: #team-product-analytics
SESSION_REPLAY_V3_INGESTION_PLAYBACK: 'session-replay-v3-ingestion-playback', // owner: @benjackwhite
SESSION_REPLAY_FILTER_ORDERING: 'session-replay-filter-ordering', // owner: @team-replay
} as const
export type FeatureFlagKey = (typeof FEATURE_FLAGS)[keyof typeof FEATURE_FLAGS]

Expand Down
27 changes: 15 additions & 12 deletions frontend/src/lib/lemon-ui/LemonRadio/LemonRadio.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ export interface LemonRadioOption<T extends React.Key> {
label: string | JSX.Element
value: T
disabledReason?: string
'data-attr'?: string
'aria-label'?: string
}

export interface LemonRadioProps<T extends React.Key> {
Expand All @@ -16,41 +18,42 @@ export interface LemonRadioProps<T extends React.Key> {

/** Single choice radio. */
export function LemonRadio<T extends React.Key>({
value,
value: selectedValue,
onChange,
options,
className,
}: LemonRadioProps<T>): JSX.Element {
return (
<div className={clsx('flex flex-col gap-2 font-medium', className)}>
{options.map((option) => {
{options.map(({ value, label, disabledReason, ...optionProps }) => {
const content = (
<label
key={option.value}
key={value}
className={clsx(
'flex items-center space-x-2',
option.disabledReason ? 'text-muted cursor-not-allowed' : 'cursor-pointer'
disabledReason ? 'text-muted cursor-not-allowed' : 'cursor-pointer'
)}
>
<input
type="radio"
className="cursor-pointer"
checked={option.value === value}
value={option.value}
checked={value === selectedValue}
value={value}
onChange={() => {
if (!option.disabledReason) {
onChange(option.value)
if (!disabledReason) {
onChange(value)
}
}}
disabled={!!option.disabledReason}
disabled={!!disabledReason}
{...optionProps}
/>
<span>{option.label}</span>
<span>{label}</span>
</label>
)

if (option.disabledReason) {
if (disabledReason) {
return (
<Tooltip key={option.value} title={option.disabledReason}>
<Tooltip key={value} title={disabledReason}>
{content}
</Tooltip>
)
Expand Down
38 changes: 38 additions & 0 deletions frontend/src/scenes/session-recordings/filters/OrderingFilters.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { useActions, useValues } from 'kea'
import { LemonRadio } from 'lib/lemon-ui/LemonRadio'

import { sessionRecordingsPlaylistLogic } from '../playlist/sessionRecordingsPlaylistLogic'

export const OrderingFilters = (): JSX.Element => {
const { orderBy } = useValues(sessionRecordingsPlaylistLogic)
const { setOrderBy } = useActions(sessionRecordingsPlaylistLogic)

return (
<LemonRadio
value={orderBy}
onChange={setOrderBy}
options={[
{
value: 'start_time',
label: 'Latest',
'data-attr': 'session-replay-ordering-latest',
},
{
value: 'console_error_count',
label: 'Most console errors',
'data-attr': 'session-replay-ordering-errors',
},
{
value: 'active_seconds',
label: 'Longest (active duration)',
'data-attr': 'session-replay-ordering-active',
},
{
value: 'duration',
label: 'Longest (total duration)',
'data-attr': 'session-replay-ordering-duration',
},
]}
/>
)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { LemonButton, LemonCollapse } from '@posthog/lemon-ui'
import equal from 'fast-deep-equal'
import { useActions, useValues } from 'kea'
import { useFeatureFlag } from 'lib/hooks/useFeatureFlag'
import { LemonLabel } from 'lib/lemon-ui/LemonLabel/LemonLabel'
import { useMemo } from 'react'

Expand All @@ -9,6 +10,7 @@ import { RecordingFilters } from '~/types'
import { playerSettingsLogic } from '../player/playerSettingsLogic'
import { getDefaultFilters } from '../playlist/sessionRecordingsPlaylistLogic'
import { AdvancedSessionRecordingsFilters } from './AdvancedSessionRecordingsFilters'
import { OrderingFilters } from './OrderingFilters'
import { SimpleSessionRecordingsFilters } from './SimpleSessionRecordingsFilters'

interface SessionRecordingsFiltersProps {
Expand Down Expand Up @@ -41,6 +43,7 @@ export function SessionRecordingsFilters({
const defaultFilters = getDefaultFilters()
return prefersAdvancedFilters || !equal(advancedFilters, defaultFilters)
}, [])
const hasFilterOrdering = useFeatureFlag('SESSION_REPLAY_FILTER_ORDERING')

const AdvancedFilters = (
<AdvancedSessionRecordingsFilters
Expand All @@ -50,6 +53,20 @@ export function SessionRecordingsFilters({
/>
)

const panels = [
!hideSimpleFilters && {
key: 'advanced-filters',
header: 'Advanced filters',
className: 'p-0',
content: AdvancedFilters,
},
hasFilterOrdering && {
key: 'ordering',
header: 'Ordering',
content: <OrderingFilters />,
},
].filter(Boolean)

return (
<div className="relative flex flex-col">
<div className="space-y-1 p-3">
Expand All @@ -70,25 +87,18 @@ export function SessionRecordingsFilters({
)}
</div>

{hideSimpleFilters ? (
AdvancedFilters
) : (
{hideSimpleFilters && AdvancedFilters}

{panels.length > 0 && (
<LemonCollapse
className="w-full rounded-none border-0 border-t"
multiple
defaultActiveKeys={initiallyOpen ? ['advanced-filters'] : []}
size="small"
panels={panels}
onChange={(activeKeys) => {
setPrefersAdvancedFilters(activeKeys.includes('advanced-filters'))
}}
panels={[
{
key: 'advanced-filters',
header: 'Advanced filters',
className: 'p-0',
content: AdvancedFilters,
},
]}
/>
)}
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { IconClock, IconKeyboard, IconPinFilled } from '@posthog/icons'
import { IconBug, IconClock, IconKeyboard, IconPinFilled } from '@posthog/icons'
import clsx from 'clsx'
import { useValues } from 'kea'
import { FlaggedFeature } from 'lib/components/FlaggedFeature'
Expand All @@ -22,6 +22,7 @@ import { urls } from 'scenes/urls'
import { DurationType, SessionRecordingType } from '~/types'

import { sessionRecordingsListPropertiesLogic } from './sessionRecordingsListPropertiesLogic'
import { sessionRecordingsPlaylistLogic } from './sessionRecordingsPlaylistLogic'

export interface SessionRecordingPreviewProps {
recording: SessionRecordingType
Expand All @@ -48,7 +49,7 @@ function RecordingDuration({
const [hours, minutes, seconds] = formattedDuration.split(':')

return (
<div className="flex items-center flex-1 justify-end font-semibold">
<div className="flex items-center flex-1 space-x-1 justify-end font-semibold">
<IconClock className={iconClassNames} />
<span>
<span className={clsx(hours === '00' && 'opacity-50 font-normal')}>{hours}:</span>
Expand All @@ -65,6 +66,25 @@ function RecordingDuration({
)
}

function ErrorCount({
iconClassNames,
errorCount,
}: {
iconClassNames: string
errorCount: number | undefined
}): JSX.Element {
if (errorCount === undefined) {
return <div className="flex items-center flex-1 justify-end font-semibold">-</div>
}

return (
<div className="flex items-center flex-1 space-x-1 justify-end font-semibold">
<IconBug className={iconClassNames} />
<span>{errorCount}</span>
</div>
)
}

interface GatheredProperty {
property: string
value: string | undefined
Expand Down Expand Up @@ -243,9 +263,10 @@ export function SessionRecordingPreview({
summariseFn,
sessionSummaryLoading,
}: SessionRecordingPreviewProps): JSX.Element {
const { orderBy } = useValues(sessionRecordingsPlaylistLogic)
const { durationTypeToShow } = useValues(playerSettingsLogic)

const iconClassnames = clsx('SessionRecordingPreview__property-icon text-base text-muted-alt')
const iconClassnames = 'SessionRecordingPreview__property-icon text-base text-muted-alt'

const [summaryPopoverIsVisible, setSummaryPopoverIsVisible] = useState<boolean>(false)

Expand Down Expand Up @@ -304,10 +325,17 @@ export function SessionRecordingPreview({
</div>
<div className="flex-1" />

<RecordingDuration
iconClassNames={iconClassnames}
recordingDuration={durationToShow(recording, durationTypeToShow)}
/>
{orderBy === 'console_error_count' ? (
<ErrorCount iconClassNames={iconClassnames} errorCount={recording.console_error_count} />
) : (
<RecordingDuration
iconClassNames={iconClassnames}
recordingDuration={durationToShow(
recording,
orderBy === 'start_time' ? durationTypeToShow : orderBy
)}
/>
)}
</div>

<div className="flex items-center justify-between gap-2">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import { Tooltip } from 'lib/lemon-ui/Tooltip'
import { DurationTypeSelect } from 'scenes/session-recordings/filters/DurationTypeSelect'

import { playerSettingsLogic } from '../player/playerSettingsLogic'
import { sessionRecordingsPlaylistLogic } from './sessionRecordingsPlaylistLogic'

export function SessionRecordingsPlaylistSettings(): JSX.Element {
const { autoplayDirection, durationTypeToShow, hideViewedRecordings } = useValues(playerSettingsLogic)
const { toggleAutoplayDirection, setDurationTypeToShow, setHideViewedRecordings } = useActions(playerSettingsLogic)
const { orderBy } = useValues(sessionRecordingsPlaylistLogic)

return (
<div className="relative flex flex-col gap-2 p-3 bg-side border-b">
Expand Down Expand Up @@ -51,14 +53,16 @@ export function SessionRecordingsPlaylistSettings(): JSX.Element {
onChange={() => setHideViewedRecordings(!hideViewedRecordings)}
/>
</div>
<div className="flex flex-row items-center justify-between space-x-2">
<span className="text-black font-medium">Show</span>
<DurationTypeSelect
value={durationTypeToShow}
onChange={(value) => setDurationTypeToShow(value)}
onChangeEventDescription="session recording list duration type to show selected"
/>
</div>
{orderBy === 'start_time' && (
<div className="flex flex-row items-center justify-between space-x-2">
<span className="text-black font-medium">Show</span>
<DurationTypeSelect
value={durationTypeToShow}
onChange={(value) => setDurationTypeToShow(value)}
onChangeEventDescription="session recording list duration type to show selected"
/>
</div>
)}
</div>
)
}
Loading

0 comments on commit dbb3c09

Please sign in to comment.