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:
parent
5da0c1259e
commit
b3b5986049
@ -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 (
|
||||
<>
|
||||
|
@ -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',
|
||||
|
@ -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 }) => ({
|
||||
|
@ -50,7 +50,8 @@ export function ActionsLineGraph({
|
||||
dataset.label,
|
||||
day,
|
||||
day,
|
||||
dataset.breakdown_value || dataset.status
|
||||
dataset.breakdown_value || dataset.status,
|
||||
true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user