Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add AI Regex Helper to path cleaning #28512

Merged
merged 16 commits into from
Feb 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { IconPlus } from '@posthog/icons'
import { LemonButton } from 'lib/lemon-ui/LemonButton'
import { Popover } from 'lib/lemon-ui/Popover/Popover'
import { useState } from 'react'

import { PathCleaningFilter } from '~/types'

import { PathRegexPopover } from './PathRegexPopover'
import { PathRegexModal } from './PathRegexModal'

type PathCleanFilterAddItemButtonProps = {
onAdd: (filter: PathCleaningFilter) => void
Expand All @@ -14,29 +13,25 @@ type PathCleanFilterAddItemButtonProps = {
export function PathCleanFilterAddItemButton({ onAdd }: PathCleanFilterAddItemButtonProps): JSX.Element {
const [visible, setVisible] = useState(false)
return (
<Popover
visible={visible}
onClickOutside={() => setVisible(false)}
overlay={
<PathRegexPopover
onSave={(filter: PathCleaningFilter) => {
onAdd(filter)
setVisible(false)
}}
onCancel={() => setVisible(false)}
isNew
/>
}
>
<>
<PathRegexModal
isOpen={visible}
onClose={() => setVisible(false)}
onSave={(filter: PathCleaningFilter) => {
onAdd(filter)
setVisible(false)
}}
/>

<LemonButton
onClick={() => setVisible(!visible)}
onClick={() => setVisible(true)}
type="secondary"
size="small"
icon={<IconPlus />}
sideIcon={null}
>
Add rule
</LemonButton>
</Popover>
</>
)
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { useSortable } from '@dnd-kit/sortable'
import { CSS } from '@dnd-kit/utilities'
import { IconArrowCircleRight } from '@posthog/icons'
import { LemonSnack, Popover, Tooltip } from '@posthog/lemon-ui'
import { LemonSnack, Tooltip } from '@posthog/lemon-ui'
import clsx from 'clsx'
import { isValidRegexp } from 'lib/utils/regexp'
import { useState } from 'react'

import { PathCleaningFilter } from '~/types'

import { PathRegexPopover } from './PathRegexPopover'
import { PathRegexModal } from './PathRegexModal'

interface PathCleanFilterItem {
filter: PathCleaningFilter
Expand All @@ -24,21 +24,18 @@ export function PathCleanFilterItem({ filter, onChange, onRemove }: PathCleanFil
const isInvalidRegex = !isValidRegexp(regex)

return (
<Popover
visible={visible}
onClickOutside={() => setVisible(false)}
overlay={
<PathRegexPopover
<>
{visible && (
<PathRegexModal
filter={filter}
isOpen={visible}
onClose={() => setVisible(false)}
onSave={(filter: PathCleaningFilter) => {
onChange(filter)
setVisible(false)
}}
onCancel={() => setVisible(false)}
/>
}
>
{/* required for popover placement */}
)}
<div
className="relative"
ref={setNodeRef}
Expand All @@ -63,7 +60,7 @@ export function PathCleanFilterItem({ filter, onChange, onRemove }: PathCleanFil
</LemonSnack>
</Tooltip>
</div>
</Popover>
</>
)
}

Expand Down
103 changes: 103 additions & 0 deletions frontend/src/lib/components/PathCleanFilters/PathRegexModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { LemonButton, LemonInput, LemonModal, Link } from '@posthog/lemon-ui'
import { FEATURE_FLAGS } from 'lib/constants'
import { isValidRegexp } from 'lib/utils/regexp'
import { useState } from 'react'
import { AiRegexHelperButton } from 'scenes/session-recordings/components/AiRegexHelper/AiRegexHelper'
import { AiRegexHelper } from 'scenes/session-recordings/components/AiRegexHelper/AiRegexHelper'

import { PathCleaningFilter } from '~/types'

import { FlaggedFeature } from '../FlaggedFeature'

export interface PathRegexModalProps {
isOpen: boolean
onSave: (filter: PathCleaningFilter) => void
onClose: () => void
filter?: PathCleaningFilter
}

export function PathRegexModal({ filter, isOpen, onSave, onClose }: PathRegexModalProps): JSX.Element {
const [alias, setAlias] = useState(filter?.alias ?? '')
const [regex, setRegex] = useState(filter?.regex ?? '')

const isNew = !filter
const disabledReason = !alias
? 'Alias is required'
: !regex
? 'Regex is required'
: !isValidRegexp(regex)
? 'Malformed regex'
: null

return (
<LemonModal isOpen={isOpen} onClose={onClose}>
<LemonModal.Header>
{isNew ? <b>Add Path Cleaning Rule</b> : <b>Edit Path Cleaning Rule</b>}
</LemonModal.Header>

<LemonModal.Content>
<div className="px-2 py-1" data-attr="path-regex-modal-content">
<div className="space-y-2">
<div>
<span>Alias</span>
<LemonInput
value={alias}
onChange={(alias) => setAlias(alias)}
onPressEnter={() => false}
rafaeelaudibert marked this conversation as resolved.
Show resolved Hide resolved
/>
rafaeelaudibert marked this conversation as resolved.
Show resolved Hide resolved
<div className="text-muted">
We suggest you use <code>&lt;id&gt;</code> or <code>&lt;slug&gt;</code> to indicate a
dynamic part of the path.
</div>
</div>
<div>
<span>Regex</span>
<LemonInput
value={regex}
onChange={(regex) => setRegex(regex)}
onPressEnter={() => false}
/>
<p className="text-secondary">
<span>
Example:{' '}
<span className="font-mono text-accent-primary text-xs">
/merchant/\d+/dashboard$
</span>{' '}
(no need to escape slashes)
</span>{' '}
<br />
<span>
We use the{' '}
<Link to="https://github.com/google/re2/wiki/Syntax" target="_blank">
rafaeelaudibert marked this conversation as resolved.
Show resolved Hide resolved
re2
</Link>{' '}
syntax.
</span>
</p>
</div>
</div>

<div className="flex space-between mt-3">
<FlaggedFeature flag={FEATURE_FLAGS.PATH_CLEANING_AI_REGEX}>
<AiRegexHelper onApply={setRegex} />
<AiRegexHelperButton />
</FlaggedFeature>

<div className="flex flex-1 justify-end gap-2">
<LemonButton type="secondary" onClick={onClose}>
Cancel
</LemonButton>
<LemonButton
type="primary"
onClick={() => onSave({ alias, regex })}
disabledReason={disabledReason}
>
Save
</LemonButton>
</div>
</div>
</div>
</LemonModal.Content>
</LemonModal>
)
}
73 changes: 0 additions & 73 deletions frontend/src/lib/components/PathCleanFilters/PathRegexPopover.tsx

This file was deleted.

1 change: 1 addition & 0 deletions frontend/src/lib/constants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ export const FEATURE_FLAGS = {
WEB_ANALYTICS_IMPROVED_PATH_CLEANING: 'web-analytics-improved-path-cleaning', // owner: @rafaeelaudibert #team-web-analytics
EXPERIMENTAL_DASHBOARD_ITEM_RENDERING: 'experimental-dashboard-item-rendering', // owner: @thmsobrmlr #team-product-analytics
RECORDINGS_AI_FILTER: 'recordings-ai-filter', // owner: @veryayskiy #team-replay
PATH_CLEANING_AI_REGEX: 'path-cleaning-ai-regex', // owner: @rafaeelaudibert #team-web-analytics
} as const
export type FeatureFlagKey = (typeof FEATURE_FLAGS)[keyof typeof FEATURE_FLAGS]

Expand Down
Loading
Loading