Skip to content

Commit

Permalink
feat: Person node changes (#17880)
Browse files Browse the repository at this point in the history
Person node changes
  • Loading branch information
benjackwhite authored Oct 10, 2023
1 parent b09c7a8 commit 8a55fc1
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 28 deletions.
101 changes: 83 additions & 18 deletions frontend/src/scenes/notebooks/Nodes/NotebookNodePerson.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
import { createPostHogWidgetNode } from 'scenes/notebooks/Nodes/NodeWrapper'
import { NotebookNodeType, PropertyDefinitionType } from '~/types'
import { useValues } from 'kea'
import { useActions, useValues } from 'kea'
import { LemonDivider } from '@posthog/lemon-ui'
import { urls } from 'scenes/urls'
import { PersonDisplay, TZLabel } from '@posthog/apps-common'
import { PersonIcon, TZLabel } from '@posthog/apps-common'
import { personLogic } from 'scenes/persons/personLogic'
import { PropertiesTable } from 'lib/components/PropertiesTable'
import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton'
import { notebookNodeLogic } from './notebookNodeLogic'
import { NotebookNodeViewProps } from '../Notebook/utils'
import { asDisplay } from 'scenes/persons/person-utils'
import { useEffect } from 'react'
import { PropertyIcon } from 'lib/components/PropertyIcon'
import clsx from 'clsx'
import { NodeKind } from '~/queries/schema'

const Component = (props: NotebookNodeViewProps<NotebookNodePersonAttributes>): JSX.Element => {
const { id } = props.attributes

const logic = personLogic({ id })
const { person, personLoading } = useValues(logic)
const { expanded } = useValues(notebookNodeLogic)
// const { setActions, insertAfter } = useActions(notebookNodeLogic)
const { setExpanded, setActions, insertAfter } = useActions(notebookNodeLogic)

const title = person ? `Person: ${asDisplay(person)}` : 'Person'

Expand All @@ -28,34 +31,96 @@ const Component = (props: NotebookNodeViewProps<NotebookNodePersonAttributes>):
}, 0)
}, [title])

// useEffect(() => {
// setActions([
// {
// text: "Events",
// onClick: () => {
// insertAfter({
// type: NotebookNodeType.Events,
// })
// }
// ])
// }, [person])
useEffect(() => {
setActions([
{
text: 'Events',
onClick: () => {
setExpanded(false)
insertAfter({
type: NotebookNodeType.Query,
attrs: {
title: `Events for ${title}`,
query: {
kind: NodeKind.DataTableNode,
source: {
kind: NodeKind.EventsQuery,
select: [
'*',
'event',
'person',
'coalesce(properties.$current_url, properties.$screen_name) -- Url / Screen',
'properties.$lib',
'timestamp',
],
personId: person?.uuid,
after: '-24h',
},
},
},
})
},
},
])
}, [person])

useEffect(() => {
props.updateAttributes({
title: person ? `Person: ${asDisplay(person)}` : 'Person',
})
}, [person])

const iconPropertyKeys = ['$geoip_country_code', '$browser', '$device_type', '$os']
const iconProperties = person?.properties || {}

const propertyIcons = (
<div className="flex flex-row flex-nowrap shrink-0 gap-1 h-4 ph-no-capture">
{!personLoading ? (
iconPropertyKeys.map((property) => {
let value = iconProperties?.[property]
if (property === '$device_type') {
value = iconProperties?.['$device_type'] || iconProperties?.['$initial_device_type']
}

let tooltipValue = value
if (property === '$geoip_country_code') {
tooltipValue = `${iconProperties?.['$geoip_country_name']} (${value})`
}

return (
<PropertyIcon
key={property}
className={'text-muted-alt'}
property={property}
value={value}
tooltipTitle={() => (
<div className="text-center">
<span className="font-medium">{tooltipValue ?? 'N/A'}</span>
</div>
)}
/>
)
})
) : (
<LemonSkeleton className="w-18 my-1" />
)}
</div>
)

return (
<div className="flex flex-col overflow-hidden">
<div className="p-4 flex-0 flex gap-2 justify-between">
<div className={clsx('p-4 flex-0 flex gap-2 justify-between ', !expanded && 'cursor-pointer')}>
{personLoading ? (
<LemonSkeleton className="h-6" />
) : (
<>
<span className="font-semibold">
<PersonDisplay withIcon person={person} noLink noPopover />
</span>
<div className="flex gap-2">
<PersonIcon person={person} size="xl" />
<div>
<div className="font-semibold">{asDisplay(person)}</div>
<div>{propertyIcons}</div>
</div>
</div>

{person ? (
<div>
Expand Down
8 changes: 4 additions & 4 deletions frontend/src/scenes/notebooks/Notebook/NotebookPopover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,17 @@ export function NotebookPopoverCard(): JSX.Element | null {

const editable = visibility !== 'hidden' && !notebook?.is_template

if (droppedResource) {
return null
}

const { ref, size } = useResizeBreakpoints({
0: 'small',
832: 'medium',
})

const contentWidthHasEffect = useMemo(() => fullScreen && size === 'medium', [fullScreen, size])

if (droppedResource) {
return null
}

return (
<div ref={ref} className="NotebookPopover__content__card">
<header className="flex items-center justify-between gap-2 font-semibold shrink-0 p-1 border-b">
Expand Down
19 changes: 13 additions & 6 deletions frontend/src/scenes/persons/PersonDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@ export interface PersonDisplayProps {
noPopover?: boolean
}

export function PersonDisplay({ person, withIcon, noEllipsis, noPopover, noLink }: PersonDisplayProps): JSX.Element {
const href = asLink(person)
export function PersonIcon({
person,
...props
}: Pick<PersonDisplayProps, 'person'> & Omit<ProfilePictureProps, 'name' | 'email'>): JSX.Element {
const display = asDisplay(person)
const [visible, setVisible] = useState(false)

const email: string | undefined = useMemo(() => {
// The email property could be correct but it could also be set strangely such as an array or not even a string
Expand All @@ -33,11 +34,17 @@ export function PersonDisplay({ person, withIcon, noEllipsis, noPopover, noLink
return typeof possibleEmail === 'string' ? possibleEmail : undefined
}, [person?.properties?.email])

return <ProfilePicture {...props} name={display} email={email} />
}

export function PersonDisplay({ person, withIcon, noEllipsis, noPopover, noLink }: PersonDisplayProps): JSX.Element {
const href = asLink(person)
const display = asDisplay(person)
const [visible, setVisible] = useState(false)

let content = (
<span className="flex items-center">
{withIcon && (
<ProfilePicture name={display} email={email} size={typeof withIcon === 'string' ? withIcon : 'md'} />
)}
{withIcon && <PersonIcon person={person} size={typeof withIcon === 'string' ? withIcon : 'md'} />}
<span className={clsx('ph-no-capture', !noEllipsis && 'truncate')}>{display}</span>
</span>
)
Expand Down

0 comments on commit 8a55fc1

Please sign in to comment.