feat(project-tree): persons menu (#32690)

This commit is contained in:
Marius Andra
2025-05-27 10:58:03 +02:00
committed by GitHub
parent f2a40a92b2
commit 73ff295284
11 changed files with 79 additions and 45 deletions

View File

@@ -169,6 +169,9 @@ export function PanelLayout({ mainRef }: { mainRef: React.RefObject<HTMLElement>
{activePanelIdentifier === 'Data management' && (
<ProjectTree root="data-management://" searchPlaceholder="Search data management" />
)}
{activePanelIdentifier === 'Persons' && (
<ProjectTree root="persons://" searchPlaceholder="Search persons" />
)}
</PanelLayoutNavBar>
</div>

View File

@@ -292,14 +292,16 @@ export function PanelLayoutNavBar({ children }: { children: React.ReactNode }):
]
: []),
{
identifier: 'PersonsManagement',
identifier: 'Persons',
id: featureFlags[FEATURE_FLAGS.TREE_VIEW_PRODUCTS] ? 'Persons' : 'Persons and groups',
icon: <IconPeople />,
to: urls.persons(),
onClick: () => {
handleStaticNavbarItemClick(urls.persons(), true)
onClick: (e?: React.KeyboardEvent) => {
if (!e || e.key === 'Enter' || e.key === ' ' || e.key === 'ArrowRight') {
handlePanelTriggerClick('Persons')
}
},
tooltip: featureFlags[FEATURE_FLAGS.TREE_VIEW_PRODUCTS] ? 'Persons' : 'Persons and groups',
showChevron: true,
tooltip: isLayoutPanelVisible && activePanelIdentifier === 'Persons' ? 'Close persons' : 'Open persons',
tooltipDocLink: 'https://posthog.com/docs/data/persons',
},
{

View File

@@ -632,10 +632,7 @@ export function ProjectTree({
if (root === 'games://') {
return <>Play {nameNode}</>
}
if (root === 'products://') {
return <>View {nameNode}</>
}
if (root === 'data-management://') {
if (root === 'products://' || root === 'data-management://' || root === 'persons://') {
return <>View {nameNode}</>
}
if (root === 'new://') {

View File

@@ -251,3 +251,18 @@ export const getDefaultTreeProducts = (): FileSystemImport[] =>
export const getDefaultTreeGames = (): FileSystemImport[] =>
[...getTreeItemsGames()].sort((a, b) => a.path.localeCompare(b.path, undefined, { sensitivity: 'accent' }))
export const getDefaultTreePersons = (): FileSystemImport[] => [
{
path: 'Persons',
iconType: 'cohort',
href: urls.persons(),
visualOrder: 10,
},
{
path: 'Cohorts',
type: 'cohort',
href: urls.cohorts(),
visualOrder: 20,
},
]

View File

@@ -2,15 +2,19 @@ import { actions, afterMount, connect, kea, listeners, path, reducers, selectors
import { loaders } from 'kea-loaders'
import api from 'lib/api'
import { FEATURE_FLAGS } from 'lib/constants'
import { GroupsAccessStatus } from 'lib/introductions/groupsAccessLogic'
import { lemonToast } from 'lib/lemon-ui/LemonToast'
import { TreeDataItem } from 'lib/lemon-ui/LemonTree/LemonTree'
import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
import { capitalizeFirstLetter } from 'lib/utils'
import { urls } from 'scenes/urls'
import { breadcrumbsLogic } from '~/layout/navigation/Breadcrumbs/breadcrumbsLogic'
import {
getDefaultTreeDataManagement,
getDefaultTreeGames,
getDefaultTreeNew,
getDefaultTreePersons,
getDefaultTreeProducts,
} from '~/layout/panel-layout/ProjectTree/defaultTree'
import { projectTreeLogic, RecentResults, SearchResults } from '~/layout/panel-layout/ProjectTree/projectTreeLogic'
@@ -24,6 +28,7 @@ import {
sortFilesAndFolders,
splitPath,
} from '~/layout/panel-layout/ProjectTree/utils'
import { groupsModel } from '~/models/groupsModel'
import { FileSystemEntry, FileSystemImport } from '~/queries/schema/schema-general'
import { UserBasicType } from '~/types'
@@ -36,7 +41,14 @@ export const PAGINATION_LIMIT = 100
export const projectTreeDataLogic = kea<projectTreeDataLogicType>([
path(['layout', 'panel-layout', 'ProjectTree', 'projectTreeDataLogic']),
connect(() => ({
values: [featureFlagLogic, ['featureFlags'], breadcrumbsLogic, ['projectTreeRef']],
values: [
featureFlagLogic,
['featureFlags'],
breadcrumbsLogic,
['projectTreeRef'],
groupsModel,
['aggregationLabel', 'groupTypes', 'groupTypesLoading', 'groupsAccessStatus'],
],
})),
actions({
loadUnfiledItems: true,
@@ -522,9 +534,40 @@ export const projectTreeDataLogic = kea<projectTreeDataLogicType>([
return treeItem ?? null
},
],
groupItems: [
(s) => [s.groupTypes, s.groupsAccessStatus, s.aggregationLabel],
(groupTypes, groupsAccessStatus, aggregationLabel): FileSystemImport[] => {
const showGroupsIntroductionPage = [
GroupsAccessStatus.HasAccess,
GroupsAccessStatus.HasGroupTypes,
GroupsAccessStatus.NoAccess,
].includes(groupsAccessStatus)
const groupItems: FileSystemImport[] = showGroupsIntroductionPage
? [
{
path: 'Groups',
iconType: 'cohort',
href: urls.groups(0),
visualOrder: 30,
},
]
: Array.from(groupTypes.values()).map((groupType) => ({
path: capitalizeFirstLetter(aggregationLabel(groupType.group_type_index).plural),
iconType: 'cohort',
href: urls.groups(groupType.group_type_index),
visualOrder: 30 + groupType.group_type_index,
}))
return groupItems
},
],
getStaticTreeItems: [
(s) => [s.featureFlags, s.shortcutData],
(featureFlags, shortcutData): ((searchTerm: string, onlyFolders: boolean) => TreeDataItem[]) => {
(s) => [s.featureFlags, s.shortcutData, s.groupItems],
(
featureFlags,
shortcutData,
groupItems
): ((searchTerm: string, onlyFolders: boolean) => TreeDataItem[]) => {
const convert = (
imports: FileSystemImport[],
root: string,
@@ -547,6 +590,7 @@ export const projectTreeDataLogic = kea<projectTreeDataLogicType>([
const data: [string, FileSystemImport[]][] = [
['products://', getDefaultTreeProducts()],
['data-management://', getDefaultTreeDataManagement()],
['persons://', [...getDefaultTreePersons(), ...groupItems]],
...(featureFlags[FEATURE_FLAGS.GAME_CENTER]
? ([['games://', getDefaultTreeGames()]] as [string, FileSystemImport[]][])
: ([] as [string, FileSystemImport[]][])),

View File

@@ -273,7 +273,8 @@ export function convertFileSystemEntryToTreeDataItem({
}
}
}
if (root !== 'products://') {
if (root !== 'products://' && root !== 'persons://') {
sortNodes(rootNodes)
}

View File

@@ -8,6 +8,7 @@ export type PanelLayoutNavIdentifier =
| 'Project'
| 'Recent'
| 'Products'
| 'Persons'
| 'Games'
| 'Shortcuts'
| 'Data management'

View File

@@ -453,7 +453,6 @@ export const getTreeItemsProducts = (): FileSystemImport[] => [
href: urls.featureFlags(),
visualOrder: PRODUCT_VISUAL_ORDER.featureFlags,
},
{ path: 'Group analytics', iconType: 'cohort', href: urls.groups(0), visualOrder: PRODUCT_VISUAL_ORDER.groups },
{
path: 'LLM observability',
iconType: 'ai',
@@ -475,7 +474,6 @@ export const getTreeItemsProducts = (): FileSystemImport[] => [
flag: FEATURE_FLAGS.LOGS,
visualOrder: PRODUCT_VISUAL_ORDER.logs,
},
{ path: 'Persons', iconType: 'cohort', href: urls.persons(), visualOrder: PRODUCT_VISUAL_ORDER.persons },
{
path: 'Product analytics',
type: 'insight',
@@ -515,7 +513,6 @@ export const getTreeItemsGames = (): FileSystemImport[] => [{ path: '368 Hedgeho
/** This const is auto-generated, as is the whole file */
export const getTreeItemsDataManagement = (): FileSystemImport[] => [
{ path: 'Actions', iconType: 'rocket', href: urls.actions() },
{ path: 'Cohorts', type: 'cohort', href: urls.cohorts() },
{ path: 'Revenue settings', iconType: 'handMoney', href: urls.revenueSettings() },
]

View File

@@ -23,11 +23,5 @@ export const manifest: ProductManifest = {
},
],
treeItemsProducts: [],
treeItemsDataManagement: [
{
path: 'Cohorts',
type: 'cohort',
href: urls.cohorts(),
},
],
treeItemsDataManagement: [],
}

View File

@@ -1,6 +1,3 @@
import { PRODUCT_VISUAL_ORDER } from 'lib/constants'
import { urls } from 'scenes/urls'
import { ProductManifest } from '../../frontend/src/types'
export const manifest: ProductManifest = {
@@ -19,12 +16,5 @@ export const manifest: ProductManifest = {
fileSystemTypes: {
// TODO: create group node entries in the backend
},
treeItemsProducts: [
{
path: 'Group analytics',
iconType: 'cohort',
href: urls.groups(0),
visualOrder: PRODUCT_VISUAL_ORDER.groups,
},
],
treeItemsProducts: [],
}

View File

@@ -1,6 +1,3 @@
import { PRODUCT_VISUAL_ORDER } from 'lib/constants'
import { urls } from 'scenes/urls'
import { ProductManifest } from '../../frontend/src/types'
export const manifest: ProductManifest = {
@@ -13,12 +10,5 @@ export const manifest: ProductManifest = {
persons: (): string => '/persons',
},
fileSystemTypes: {},
treeItemsProducts: [
{
path: 'Persons',
iconType: 'cohort',
href: urls.persons(),
visualOrder: PRODUCT_VISUAL_ORDER.persons,
},
],
treeItemsProducts: [],
}