mirror of
https://github.com/BillyOutlast/posthog.git
synced 2026-02-04 03:01:23 +01:00
feat(project-tree): persons menu (#32690)
This commit is contained in:
@@ -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>
|
||||
|
||||
|
||||
@@ -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',
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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://') {
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
]
|
||||
|
||||
@@ -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[]][])),
|
||||
|
||||
@@ -273,7 +273,8 @@ export function convertFileSystemEntryToTreeDataItem({
|
||||
}
|
||||
}
|
||||
}
|
||||
if (root !== 'products://') {
|
||||
|
||||
if (root !== 'products://' && root !== 'persons://') {
|
||||
sortNodes(rootNodes)
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ export type PanelLayoutNavIdentifier =
|
||||
| 'Project'
|
||||
| 'Recent'
|
||||
| 'Products'
|
||||
| 'Persons'
|
||||
| 'Games'
|
||||
| 'Shortcuts'
|
||||
| 'Data management'
|
||||
|
||||
@@ -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() },
|
||||
]
|
||||
|
||||
|
||||
@@ -23,11 +23,5 @@ export const manifest: ProductManifest = {
|
||||
},
|
||||
],
|
||||
treeItemsProducts: [],
|
||||
treeItemsDataManagement: [
|
||||
{
|
||||
path: 'Cohorts',
|
||||
type: 'cohort',
|
||||
href: urls.cohorts(),
|
||||
},
|
||||
],
|
||||
treeItemsDataManagement: [],
|
||||
}
|
||||
|
||||
@@ -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: [],
|
||||
}
|
||||
|
||||
@@ -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: [],
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user