diff --git a/frontend/src/scenes/persons/PersonsTable.tsx b/frontend/src/scenes/persons/PersonsTable.tsx index 89e2a8a3719..f47534b8e52 100644 --- a/frontend/src/scenes/persons/PersonsTable.tsx +++ b/frontend/src/scenes/persons/PersonsTable.tsx @@ -95,29 +95,30 @@ export function PersonsTable({ return person.created_at ? : <> }, }) + + columns.push({ + key: 'actions', + title: '', + span: 2, + render: function Render(person: PersonType, ...[, index]: [PersonType, number]) { + return ( + <> + + + {allColumns ? ' view' : ''} + + + ) + }, + }) } - columns.push({ - key: 'actions', - title: '', - span: 2, - render: function Render(person: PersonType, ...[, index]: [PersonType, number]) { - return ( - <> - - - {allColumns ? ' view' : ''} - - - ) - }, - }) return ( <> diff --git a/frontend/src/scenes/trends/PersonModal.tsx b/frontend/src/scenes/trends/PersonModal.tsx index 70d85c4ebc9..4cbe50ac781 100644 --- a/frontend/src/scenes/trends/PersonModal.tsx +++ b/frontend/src/scenes/trends/PersonModal.tsx @@ -1,15 +1,15 @@ -import React from 'react' +import React, { useState } from 'react' import { useActions, useValues } from 'kea' import dayjs from 'dayjs' import { trendsLogic } from 'scenes/trends/trendsLogic' import { DownloadOutlined } from '@ant-design/icons' -import { Modal, Button, Spin } from 'antd' +import { Modal, Button, Spin, Input } from 'antd' import { PersonsTable } from 'scenes/persons/PersonsTable' -import { Link } from 'lib/components/Link' -import { ArrowRightOutlined, ClockCircleOutlined } from '@ant-design/icons' import { featureFlagLogic } from 'lib/logic/featureFlagLogic' import { ViewType } from 'scenes/insights/insightLogic' import { toParams } from 'lib/utils' +import { PersonType } from '~/types' +import Fuse from 'fuse.js' interface Props { visible: boolean @@ -17,20 +17,34 @@ interface Props { onSaveCohort: () => void } +const searchPersons = (sources: PersonType[], search: string): PersonType[] => { + return new Fuse(sources, { + keys: ['name', 'email', 'id'], + threshold: 0.3, + }) + .search(search) + .map((result) => result.item) +} + export function PersonModal({ visible, view, onSaveCohort }: Props): JSX.Element { - const { people, filters, peopleModalURL, loadingMorePeople } = useValues( + const { people, filters, loadingMorePeople, firstLoadedPeople } = useValues( trendsLogic({ dashboardItemId: null, view }) ) - const { setShowingPeople, loadMorePeople } = useActions(trendsLogic({ dashboardItemId: null, view })) + const { setPersonsModalFilters } = useActions(trendsLogic({ dashboardItemId: null, view })) + const { setShowingPeople, loadMorePeople, setPeople } = useActions(trendsLogic({ dashboardItemId: null, view })) const { featureFlags } = useValues(featureFlagLogic) - + const [searchTerm, setSearchTerm] = useState('') + const [useFuseSearch, setUseFuseSearch] = useState(false) const title = filters.shown_as === 'Stickiness' ? `"${people?.label}" stickiness ${people?.day} day${people?.day === 1 ? '' : 's'}` : filters.display === 'ActionsBarValue' || filters.display === 'ActionsPie' ? `"${people?.label}"` : `"${people?.label}" on ${people?.day ? dayjs(people.day).format('ll') : '...'}` - const closeModal = (): void => setShowingPeople(false) + const closeModal = (): void => { + setSearchTerm('') + setShowingPeople(false) + } return (
- Found {people.count === 99 ? '99+' : people.count} {people.count === 1 ? 'user' : 'users'} + Showing {people.count > 99 ? '99' : people.count} of {people.count} persons + { + setSearchTerm(e.target.value) + }} + value={searchTerm} + onSearch={() => { + setPersonsModalFilters(searchTerm) + if (!searchTerm) { + setPersonsModalFilters({ search: undefined }) + const { count, action, label, day, breakdown_value, next } = firstLoadedPeople + setPeople(firstLoadedPeople, count, action, label, day, breakdown_value, next) + } else if (searchTerm.includes("has:")) { + setPersonsModalFilters(searchTerm) + } else { + setUseFuseSearch(true) + } + + }} + /> {featureFlags['save-cohort-on-modal'] && (view === ViewType.TRENDS || view === ViewType.STICKINESS) && (
@@ -62,18 +98,6 @@ export function PersonModal({ visible, view, onSaveCohort }: Props): JSX.Element )}
-
- - View related sessions - - - View related recordings - -
- +
({ setFilters: (filters, mergeFilters = true) => ({ filters, mergeFilters }), setDisplay: (display) => ({ display }), - - loadPeople: (action, label, date_from, date_to, breakdown_value) => ({ + loadPeople: (action, label, date_from, date_to, breakdown_value, saveOriginal?: boolean) => ({ action, label, date_from, date_to, breakdown_value, + saveOriginal }), saveCohortWithFilters: (cohortName: string) => ({ cohortName }), loadMorePeople: true, @@ -224,6 +224,16 @@ export const trendsLogic = kea ({ loading }), toggleLifecycle: (lifecycleName: string) => ({ lifecycleName }), + setPersonsModalFilters: (searchTerm) => ({ searchTerm }), + saveFirstLoadedPeople: (people, count, action, label, day, breakdown_value, next) => ({ + people, + count, + action, + label, + day, + breakdown_value, + next, + }) }), reducers: ({ props }) => ({ @@ -247,9 +257,18 @@ export const trendsLogic = kea null, - setPeople: (_, people) => people, + setPeople: (_, people) => { + console.log('setting ppl') + return people + }, }, ], + firstLoadedPeople: [ + null as TrendPeople | null, + { + saveFirstLoadedPeople: (_, people) => people, + } + ], loadingMorePeople: [ false, { @@ -418,7 +437,7 @@ export const trendsLogic = kea { + loadPeople: async ({ label, action, date_from, date_to, breakdown_value, saveOriginal }, breakpoint) => { let people = [] if (values.filters.insight === ViewType.LIFECYCLE) { const filterParams = parsePeopleParams( @@ -452,6 +471,17 @@ export const trendsLogic = kea { if (values.people) { @@ -527,6 +557,8 @@ export const trendsLogic = kea { + } }), events: ({ actions, props }) => ({ diff --git a/frontend/src/scenes/trends/viz/ActionsLineGraph.tsx b/frontend/src/scenes/trends/viz/ActionsLineGraph.tsx index 54489639a01..332e8009087 100644 --- a/frontend/src/scenes/trends/viz/ActionsLineGraph.tsx +++ b/frontend/src/scenes/trends/viz/ActionsLineGraph.tsx @@ -50,7 +50,8 @@ export function ActionsLineGraph({ dataset.label, day, day, - dataset.breakdown_value || dataset.status + dataset.breakdown_value || dataset.status, + true ) } } diff --git a/posthog/api/action.py b/posthog/api/action.py index 2935dcb9418..050c390f1b5 100644 --- a/posthog/api/action.py +++ b/posthog/api/action.py @@ -286,7 +286,8 @@ class ActionViewSet(StructuredViewSetMixin, viewsets.ModelViewSet): entity = get_target_entity(request) events = filter_by_type(entity=entity, team=team, filter=filter) - people = calculate_people(team=team, events=events, filter=filter) + # pdb.set_trace() + people = calculate_people(team=team, events=events, filter=filter, request=request) serialized_people = PersonSerializer(people, context={"request": request}, many=True).data current_url = request.get_full_path() @@ -359,16 +360,16 @@ def _filter_event_prop_breakdown(events: QuerySet, filter: Filter) -> QuerySet: return events -def calculate_people(team: Team, events: QuerySet, filter: Filter, use_offset: bool = True) -> QuerySet: +def calculate_people(team: Team, events: QuerySet, filter: Filter, request: request.Request, use_offset: bool = True) -> QuerySet: events = events.values("person_id").distinct() events = _filter_cohort_breakdown(events, filter) events = _filter_person_prop_breakdown(events, filter) events = _filter_event_prop_breakdown(events, filter) - - people = Person.objects.filter( - team=team, - id__in=[p["person_id"] for p in (events[filter.offset : filter.offset + 100] if use_offset else events)], - ) + people = Person.objects.filter(team=team.id, id__in=[p["person_id"] for p in (events[filter.offset : filter.offset + 100] if use_offset else events)]) + if request.GET.get("properties"): + value = request.GET.get("properties") + filter = Filter(data={"properties": json.loads(value)}) + people = people.filter(base.properties_to_Q(filter.properties, team_id=1, is_person_query=True)) people = people.prefetch_related(Prefetch("persondistinctid_set", to_attr="distinct_ids_cache")) return people