0
0
mirror of https://github.com/PostHog/posthog.git synced 2024-11-24 00:47:50 +01:00

set up filtering for person modal

This commit is contained in:
Li Yi Yu 2021-06-21 20:17:19 -04:00
parent 5da0c1259e
commit b3b5986049
5 changed files with 114 additions and 55 deletions

View File

@ -95,29 +95,30 @@ export function PersonsTable({
return person.created_at ? <TZLabel time={person.created_at} /> : <></>
},
})
columns.push({
key: 'actions',
title: '',
span: 2,
render: function Render(person: PersonType, ...[, index]: [PersonType, number]) {
return (
<>
<Link
to={linkToPerson(person)}
target="_blank"
rel="noopener noreferrer"
data-attr={`goto-person-arrow-${index}`}
data-test-goto-person
>
<ArrowRightOutlined style={{ float: 'right' }} />
{allColumns ? ' view' : ''}
</Link>
</>
)
},
})
}
columns.push({
key: 'actions',
title: '',
span: 2,
render: function Render(person: PersonType, ...[, index]: [PersonType, number]) {
return (
<>
<Link
to={linkToPerson(person)}
target="_blank"
rel="noopener noreferrer"
data-attr={`goto-person-arrow-${index}`}
data-test-goto-person
>
<ArrowRightOutlined style={{ float: 'right' }} />
{allColumns ? ' view' : ''}
</Link>
</>
)
},
})
return (
<>

View File

@ -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 (
<Modal
title={title}
@ -51,7 +65,29 @@ export function PersonModal({ visible, view, onSaveCohort }: Props): JSX.Element
}}
>
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start' }}>
Found {people.count === 99 ? '99+' : people.count} {people.count === 1 ? 'user' : 'users'}
<span>Showing <b>{people.count > 99 ? '99' : people.count} of {people.count}</b> persons</span>
<Input.Search
allowClear
enterButton
style={{ maxWidth: 400, width: 'initial', flexGrow: 1 }}
onChange={(e) => {
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) && (
<div>
@ -62,18 +98,6 @@ export function PersonModal({ visible, view, onSaveCohort }: Props): JSX.Element
)}
</div>
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-end' }}>
<Link
to={peopleModalURL.sessions}
style={{ marginLeft: 8 }}
data-attr="persons-modal-sessions"
>
<ClockCircleOutlined /> View related sessions <ArrowRightOutlined />
</Link>
<Link to={peopleModalURL.recordings} type="primary" data-attr="persons-modal-recordings">
View related recordings <ArrowRightOutlined />
</Link>
</div>
</div>
<div className="text-right">
<Button
@ -90,7 +114,7 @@ export function PersonModal({ visible, view, onSaveCohort }: Props): JSX.Element
title="Download CSV"
/>
</div>
<PersonsTable loading={!people?.people} people={people.people} />
<PersonsTable loading={!people?.people} people={searchTerm && useFuseSearch ? searchPersons(people.people, searchTerm) : people.people} />
<div
style={{
margin: '1rem',

View File

@ -196,13 +196,13 @@ export const trendsLogic = kea<trendsLogicType<IndexedTrendResult, TrendPeople,
actions: () => ({
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<trendsLogicType<IndexedTrendResult, TrendPeople,
loadMoreBreakdownValues: true,
setBreakdownValuesLoading: (loading: boolean) => ({ 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<trendsLogicType<IndexedTrendResult, TrendPeople,
null as TrendPeople | null,
{
setFilters: () => 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<trendsLogicType<IndexedTrendResult, TrendPeople,
errorToast(undefined, "We couldn't create your cohort:")
}
},
loadPeople: async ({ label, action, date_from, date_to, breakdown_value }, breakpoint) => {
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<trendsLogicType<IndexedTrendResult, TrendPeople,
breakdown_value,
people.next
)
if (saveOriginal) {
actions.saveFirstLoadedPeople(
people.results[0]?.people,
people.results[0]?.count,
action,
label,
date_from,
breakdown_value,
people.next
)
}
},
loadMorePeople: async ({}, breakpoint) => {
if (values.people) {
@ -527,6 +557,8 @@ export const trendsLogic = kea<trendsLogicType<IndexedTrendResult, TrendPeople,
actions.setFilters(mergedFilter, true)
}
},
setPersonsModalFilters: ({ searchTerm }) => {
}
}),
events: ({ actions, props }) => ({

View File

@ -50,7 +50,8 @@ export function ActionsLineGraph({
dataset.label,
day,
day,
dataset.breakdown_value || dataset.status
dataset.breakdown_value || dataset.status,
true
)
}
}

View File

@ -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