Skip to content

Commit

Permalink
feat: Modelling per group type + group overview pages
Browse files Browse the repository at this point in the history
  • Loading branch information
timgl committed Sep 4, 2024
1 parent 989dbd9 commit 90a9ca9
Show file tree
Hide file tree
Showing 17 changed files with 778 additions and 50 deletions.
6 changes: 3 additions & 3 deletions ee/api/authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
from django.urls.base import reverse
from rest_framework.decorators import api_view
from rest_framework.exceptions import PermissionDenied
from social_django.utils import load_backend, load_strategy

from posthog.models.organization import OrganizationMembership
from social_core.backends.saml import (
OID_COMMON_NAME,
OID_GIVEN_NAME,
Expand All @@ -15,10 +18,7 @@
SAMLIdentityProvider,
)
from social_core.exceptions import AuthFailed, AuthMissingParameter
from social_django.utils import load_backend, load_strategy

from posthog.constants import AvailableFeature
from posthog.models.organization import OrganizationMembership
from posthog.models.organization_domain import OrganizationDomain


Expand Down
2 changes: 1 addition & 1 deletion frontend/src/scenes/data-warehouse/ViewLinkModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ export function ViewLinkForm(): JSX.Element {
)
}

const HogQLDropdown = ({
export const HogQLDropdown = ({
hogQLValue,
onHogQLValueChange,
tableName,
Expand Down
10 changes: 10 additions & 0 deletions frontend/src/scenes/data-warehouse/viewLinkLogic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,16 @@ export const viewLinkLogic = kea<viewLinkLogicType>([
label: table.name,
})),
],
tableOptionsWarehouseOnly: [
(s) => [s.allTables],
(tables) =>
tables
.filter((t) => t.type != 'posthog')
.map((table) => ({
value: table.name,
label: table.name,
})),
],
sourceTableKeys: [
(s) => [s.selectedSourceTable],
(selectedSourceTable): KeySelectOption[] => {
Expand Down
24 changes: 18 additions & 6 deletions frontend/src/scenes/groups/Group.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import { PageHeader } from 'lib/components/PageHeader'
import { PropertiesTable } from 'lib/components/PropertiesTable'
import { TZLabel } from 'lib/components/TZLabel'
import { isEventFilter } from 'lib/components/UniversalFilters/utils'
import { FEATURE_FLAGS } from 'lib/constants'
import { LemonBanner } from 'lib/lemon-ui/LemonBanner'
import { LemonTabs } from 'lib/lemon-ui/LemonTabs'
import { lemonToast } from 'lib/lemon-ui/LemonToast'
import { Link } from 'lib/lemon-ui/Link'
import { Spinner, SpinnerOverlay } from 'lib/lemon-ui/Spinner/Spinner'
import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
import { GroupDashboard } from 'scenes/groups/GroupDashboard'
import { groupLogic, GroupLogicProps } from 'scenes/groups/groupLogic'
import { RelatedGroups } from 'scenes/groups/RelatedGroups'
Expand Down Expand Up @@ -86,6 +88,7 @@ export function Group(): JSX.Element {
const { groupKey, groupTypeIndex } = logicProps
const { setGroupEventsQuery } = useActions(groupLogic)
const { currentTeam } = useValues(teamLogic)
const { featureFlags } = useValues(featureFlagLogic)

if (!groupData || !groupType) {
return groupDataLoading ? <SpinnerOverlay sceneLevel /> : <NotFound object="group" />
Expand Down Expand Up @@ -116,12 +119,21 @@ export function Group(): JSX.Element {
key: PersonsTabType.PROPERTIES,
label: <span data-attr="groups-properties-tab">Properties</span>,
content: (
<PropertiesTable
type={PropertyDefinitionType.Group}
properties={groupData.group_properties || {}}
embedded={false}
searchable
/>
<>
<PropertiesTable
type={PropertyDefinitionType.Group}
properties={groupData.group_properties || {}}
embedded={false}
searchable
/>
<br />

{featureFlags[FEATURE_FLAGS.CS_DASHBOARDS] && (
<Link to={urls.groups(groupTypeIndex, 'config')}>
Import properties from other sources
</Link>
)}
</>
),
},
{
Expand Down
231 changes: 204 additions & 27 deletions frontend/src/scenes/groups/Groups.tsx
Original file line number Diff line number Diff line change
@@ -1,50 +1,41 @@
import { LemonButton, LemonTabs } from '@posthog/lemon-ui'
import { useActions, useValues } from 'kea'
import { CodeSnippet, Language } from 'lib/components/CodeSnippet'
import { router } from 'kea-router'
import { PropertiesTable } from 'lib/components/PropertiesTable'
import { TZLabel } from 'lib/components/TZLabel'
import { FEATURE_FLAGS } from 'lib/constants'
import { groupsAccessLogic, GroupsAccessStatus } from 'lib/introductions/groupsAccessLogic'
import { IconOpenInNew } from 'lib/lemon-ui/icons'
import { LemonBanner } from 'lib/lemon-ui/LemonBanner'
import { LemonDivider } from 'lib/lemon-ui/LemonDivider'
import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput'
import { LemonTable } from 'lib/lemon-ui/LemonTable'
import { LemonTableLink } from 'lib/lemon-ui/LemonTable/LemonTableLink'
import { LemonTableColumns } from 'lib/lemon-ui/LemonTable/types'
import { Link } from 'lib/lemon-ui/Link'
import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
import { capitalizeFirstLetter } from 'lib/utils'
import { GroupsIntroduction } from 'scenes/groups/GroupsIntroduction'
import { getDefaultEvent } from 'scenes/insights/utils/cleanFilters'
import { groupDisplayId } from 'scenes/persons/GroupActorDisplay'
import { urls } from 'scenes/urls'

import { Query } from '~/queries/Query/Query'
import { NodeKind } from '~/queries/schema'
import { Group, PropertyDefinitionType } from '~/types'

import { GroupsConfiguration, Snippet } from './GroupsConfiguration'
import { groupsListLogic } from './groupsListLogic'
import { groupsLogic } from './groupsLogic'

export function Groups({ groupTypeIndex }: { groupTypeIndex: number }): JSX.Element {
export function GroupsTable({ groupTypeIndex }: { groupTypeIndex: number }): JSX.Element {
const {
groupTypeName: { singular, plural },
groups,
groupsLoading,
search,
} = useValues(groupsListLogic({ groupTypeIndex }))
const { loadGroups, setSearch } = useActions(groupsListLogic({ groupTypeIndex }))
const { groupsAccessStatus } = useValues(groupsAccessLogic)

if (groupTypeIndex === undefined) {
throw new Error('groupTypeIndex is undefined')
}

if (
groupsAccessStatus == GroupsAccessStatus.HasAccess ||
groupsAccessStatus == GroupsAccessStatus.HasGroupTypes ||
groupsAccessStatus == GroupsAccessStatus.NoAccess
) {
return (
<>
<GroupsIntroduction />
</>
)
}

const columns: LemonTableColumns<Group> = [
{
title: capitalizeFirstLetter(plural),
Expand All @@ -66,7 +57,6 @@ export function Groups({ groupTypeIndex }: { groupTypeIndex: number }): JSX.Elem
},
},
]

return (
<>
<LemonInput
Expand Down Expand Up @@ -113,16 +103,203 @@ export function Groups({ groupTypeIndex }: { groupTypeIndex: number }): JSX.Elem
<Link to="https://posthog.com/docs/user-guides/group-analytics" target="_blank">
Read more here.
</Link>
<br />
<Snippet singular={singular} />
</LemonBanner>
<CodeSnippet language={Language.JavaScript} wrap>
{`posthog.group('${singular}', 'id:5', {\n` +
` name: 'Awesome ${singular}',\n` +
' value: 11\n' +
'});'}
</CodeSnippet>
</>
}
/>
</>
)
}

export function GroupsOverview({ groupTypeIndex }: { groupTypeIndex: number }): JSX.Element {
const {
groupTypeName: { plural },
search,
} = useValues(groupsListLogic({ groupTypeIndex }))
const { setSearch } = useActions(groupsListLogic({ groupTypeIndex }))
const onSearch = (): void => {
if (search && search !== '') {
router.actions.push(urls.groups(String(groupTypeIndex), 'list'))
}
}

const queries = [
{
title: (
<>
Top {plural} by {getDefaultEvent().name}
</>
),
query: {
kind: NodeKind.InsightVizNode,
source: {
kind: NodeKind.TrendsQuery,
interval: 'week',
filterTestAccounts: false,
series: [
{
event: getDefaultEvent().id,
kind: NodeKind.EventsNode,
math: 'total',
name: getDefaultEvent().name,
},
],
breakdownFilter: {
breakdown_type: 'hogql',
breakdown: `coalesce(group_${groupTypeIndex}.properties.name, $group_0)`,
},
dateRange: {
date_from: '-30d',
},
trendsFilter: {
display: 'ActionsTable',
},
},
full: false,
showDateRange: true,
},
},
{
title: <>Top {plural} by users</>,
query: {
kind: NodeKind.InsightVizNode,
source: {
kind: NodeKind.TrendsQuery,
interval: 'week',
filterTestAccounts: false,
series: [
{
event: getDefaultEvent().id,
kind: NodeKind.EventsNode,
math: 'dau',
math_group_type_index: groupTypeIndex,
name: getDefaultEvent().name,
},
],
breakdownFilter: {
breakdown_type: 'hogql',
breakdown: `coalesce(group_${groupTypeIndex}.properties.name, $group_0)`,
},
dateRange: {
date_from: '-30d',
},
trendsFilter: {
display: 'ActionsTable',
},
},
full: false,
showDateRange: true,
},
},
{
title: (
<>
Unique {plural} {getDefaultEvent().name}
</>
),
query: {
kind: NodeKind.InsightVizNode,
source: {
kind: NodeKind.TrendsQuery,
interval: 'week',
filterTestAccounts: false,
series: [
{
event: getDefaultEvent().id,
kind: NodeKind.EventsNode,
math: 'unique_group',
math_group_type_index: groupTypeIndex,
name: getDefaultEvent().name,
},
],
dateRange: {
date_from: '-30d',
},
},
full: false,
showDateRange: true,
},
},
]

return (
<>
<LemonInput
type="search"
placeholder={`Search for ${plural}`}
onChange={setSearch}
onBlur={onSearch}
onPressEnter={onSearch}
value={search}
data-attr="group-search"
className="mb-4"
/>
{queries.map(({ title, query }, index) => (
<>
<h2>{title}</h2>
<Query query={query} uniqueKey={`group-${groupTypeIndex}-query-${index}`} readOnly={true} />
<div className="flex flex-row justify-end mt-3">
<LemonButton
to={urls.insightNew(undefined, undefined, query)}
icon={<IconOpenInNew />}
size="small"
type="secondary"
>
Open as new Insight
</LemonButton>
</div>
</>
))}
</>
)
}

export function Groups({ groupTypeIndex }: { groupTypeIndex: number }): JSX.Element {
const { groupsAccessStatus } = useValues(groupsAccessLogic)
const { groupTab } = useValues(groupsLogic)
const { featureFlags } = useValues(featureFlagLogic)

if (groupTypeIndex === undefined) {
throw new Error('groupTypeIndex is undefined')
}

if (
groupsAccessStatus == GroupsAccessStatus.HasAccess ||
groupsAccessStatus == GroupsAccessStatus.HasGroupTypes ||
groupsAccessStatus == GroupsAccessStatus.NoAccess
) {
return (
<>
<GroupsIntroduction />
</>
)
}
if (featureFlags[FEATURE_FLAGS.CS_DASHBOARDS]) {
return (
<LemonTabs
activeKey={groupTab ?? 'overview'}
onChange={(tab) => router.actions.push(urls.groups(String(groupTypeIndex), tab))}
tabs={[
{
key: 'overview',
label: <>Overview</>,
content: <GroupsOverview groupTypeIndex={groupTypeIndex} />,
},
{
key: 'list',
label: <>List</>,
content: <GroupsTable groupTypeIndex={groupTypeIndex} />,
},
{
key: 'config',
label: <>Configuration</>,
content: <GroupsConfiguration groupTypeIndex={groupTypeIndex} />,
},
]}
/>
)
}
return <GroupsTable groupTypeIndex={groupTypeIndex} />
}
Loading

0 comments on commit 90a9ca9

Please sign in to comment.