diff --git a/frontend/src/scenes/notebooks/Nodes/NotebookNodePerson.tsx b/frontend/src/scenes/notebooks/Nodes/NotebookNodePerson.tsx index ba9270a42d3f6..e71f8320051e4 100644 --- a/frontend/src/scenes/notebooks/Nodes/NotebookNodePerson.tsx +++ b/frontend/src/scenes/notebooks/Nodes/NotebookNodePerson.tsx @@ -1,9 +1,9 @@ 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' @@ -11,6 +11,9 @@ 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): JSX.Element => { const { id } = props.attributes @@ -18,7 +21,7 @@ const Component = (props: NotebookNodeViewProps): 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' @@ -28,17 +31,38 @@ const Component = (props: NotebookNodeViewProps): }, 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({ @@ -46,16 +70,57 @@ const Component = (props: NotebookNodeViewProps): }) }, [person]) + const iconPropertyKeys = ['$geoip_country_code', '$browser', '$device_type', '$os'] + const iconProperties = person?.properties || {} + + const propertyIcons = ( +
+ {!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 ( + ( +
+ {tooltipValue ?? 'N/A'} +
+ )} + /> + ) + }) + ) : ( + + )} +
+ ) + return (
-
+
{personLoading ? ( ) : ( <> - - - +
+ +
+
{asDisplay(person)}
+
{propertyIcons}
+
+
{person ? (
diff --git a/frontend/src/scenes/notebooks/Notebook/NotebookPopover.tsx b/frontend/src/scenes/notebooks/Notebook/NotebookPopover.tsx index aed37034404a6..37015d2dbb177 100644 --- a/frontend/src/scenes/notebooks/Notebook/NotebookPopover.tsx +++ b/frontend/src/scenes/notebooks/Notebook/NotebookPopover.tsx @@ -24,10 +24,6 @@ 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', @@ -35,6 +31,10 @@ export function NotebookPopoverCard(): JSX.Element | null { const contentWidthHasEffect = useMemo(() => fullScreen && size === 'medium', [fullScreen, size]) + if (droppedResource) { + return null + } + return (
diff --git a/frontend/src/scenes/persons/PersonDisplay.tsx b/frontend/src/scenes/persons/PersonDisplay.tsx index 9a8464a06b89b..0bc990b360b31 100644 --- a/frontend/src/scenes/persons/PersonDisplay.tsx +++ b/frontend/src/scenes/persons/PersonDisplay.tsx @@ -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 & Omit): 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 @@ -33,11 +34,17 @@ export function PersonDisplay({ person, withIcon, noEllipsis, noPopover, noLink return typeof possibleEmail === 'string' ? possibleEmail : undefined }, [person?.properties?.email]) + return +} + +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 = ( - {withIcon && ( - - )} + {withIcon && } {display} )