diff --git a/src/app/(main)/SideNav.tsx b/src/app/(main)/SideNav.tsx index f91ccc08e..2a00ca424 100644 --- a/src/app/(main)/SideNav.tsx +++ b/src/app/(main)/SideNav.tsx @@ -14,15 +14,23 @@ import { import Link from 'next/link'; import type { Key } from 'react'; import { IconLabel } from '@/components/common/IconLabel'; -import { useGlobalState, useMessages, useNavigation } from '@/components/hooks'; -import { Globe, Grid2x2, LayoutDashboard, LinkIcon, PanelLeft } from '@/components/icons'; +import { useGlobalState, useMessages, useNavigation, useWebsiteNavItems } from '@/components/hooks'; +import { + ArrowLeft, + Globe, + Grid2x2, + LayoutDashboard, + LinkIcon, + PanelLeft, +} from '@/components/icons'; import { LanguageButton } from '@/components/input/LanguageButton'; import { NavButton } from '@/components/input/NavButton'; +import { WebsiteSelect } from '@/components/input/WebsiteSelect'; import { Logo } from '@/components/svg'; export function SideNav(props: any) { const { formatMessage, labels } = useMessages(); - const { pathname, renderUrl, websiteId, router } = useNavigation(); + const { pathname, renderUrl, websiteId, teamId, router } = useNavigation(); const [isCollapsed, setIsCollapsed] = useGlobalState('sidenav-collapsed'); const hasNav = !!(websiteId || pathname.startsWith('/admin') || pathname.includes('/settings')); @@ -58,6 +66,10 @@ export function SideNav(props: any) { router.push(id === 'user' ? '/websites' : `/teams/${id}/websites`); }; + const handleWebsiteChange = (value: string) => { + router.push(renderUrl(`/websites/${value}`)); + }; + return ( - - + + {!isCollapsed && ( }> @@ -84,18 +101,120 @@ export function SideNav(props: any) { - + - - {links.map(({ id, path, label, icon }) => { + {websiteId ? ( + + ) : ( + + {links.map(({ id, path, label, icon }) => { + return ( + + + + + + + + {label} + + + ); + })} + + )} + + + + + + + ); +} + +function WebsiteNavSection({ + websiteId, + teamId, + isCollapsed, + onWebsiteChange, +}: { + websiteId: string; + teamId: string; + isCollapsed: boolean; + onWebsiteChange: (value: string) => void; +}) { + const { formatMessage, labels } = useMessages(); + const { renderUrl } = useNavigation(); + const { items, selectedKey } = useWebsiteNavItems(websiteId); + + const renderValue = (value: any) => { + return ( + + {value?.selectedItem?.name} + + ); + }; + + return ( + + + + + + } + label={isCollapsed ? '' : formatMessage(labels.back)} + padding + /> + + + {formatMessage(labels.back)} + + + {!isCollapsed && ( + + + + )} + {items.map(({ label: sectionLabel, items: sectionItems }, index) => ( + + {!isCollapsed && ( + + {sectionLabel} + + )} + {sectionItems.map(({ id, path, label, icon }) => { + const isSelected = selectedKey === id; return ( - + @@ -108,11 +227,7 @@ export function SideNav(props: any) { ); })} - - - - - + ))} ); } diff --git a/src/app/(main)/websites/[websiteId]/WebsiteLayout.tsx b/src/app/(main)/websites/[websiteId]/WebsiteLayout.tsx index ce4451887..18c3e25d1 100644 --- a/src/app/(main)/websites/[websiteId]/WebsiteLayout.tsx +++ b/src/app/(main)/websites/[websiteId]/WebsiteLayout.tsx @@ -1,23 +1,17 @@ 'use client'; -import { Column, Grid } from '@umami/react-zen'; +import { Column } from '@umami/react-zen'; import type { ReactNode } from 'react'; import { WebsiteProvider } from '@/app/(main)/websites/WebsiteProvider'; import { PageBody } from '@/components/common/PageBody'; import { WebsiteHeader } from './WebsiteHeader'; -import { WebsiteNav } from './WebsiteNav'; export function WebsiteLayout({ websiteId, children }: { websiteId: string; children: ReactNode }) { return ( - - - - - - - {children} - - + + + {children} + ); } diff --git a/src/app/(main)/websites/[websiteId]/WebsiteNav.tsx b/src/app/(main)/websites/[websiteId]/WebsiteNav.tsx index b12bce28a..a301e015c 100644 --- a/src/app/(main)/websites/[websiteId]/WebsiteNav.tsx +++ b/src/app/(main)/websites/[websiteId]/WebsiteNav.tsx @@ -1,18 +1,7 @@ import { Column, Text } from '@umami/react-zen'; import { NavMenu } from '@/components/common/NavMenu'; -import { useMessages, useNavigation } from '@/components/hooks'; -import { - AlignEndHorizontal, - ChartPie, - Clock, - Eye, - Sheet, - Tag, - User, - UserPlus, -} from '@/components/icons'; +import { useNavigation, useWebsiteNavItems } from '@/components/hooks'; import { WebsiteSelect } from '@/components/input/WebsiteSelect'; -import { Funnel, Lightning, Magnet, Money, Network, Path, Target } from '@/components/svg'; export function WebsiteNav({ websiteId, @@ -21,130 +10,8 @@ export function WebsiteNav({ websiteId: string; onItemClick?: () => void; }) { - const { formatMessage, labels } = useMessages(); - const { pathname, renderUrl, teamId, router } = useNavigation(); - - const renderPath = (path: string) => - renderUrl(`/websites/${websiteId}${path}`, { - event: undefined, - compare: undefined, - view: undefined, - unit: undefined, - excludeBounce: undefined, - }); - - const items = [ - { - label: formatMessage(labels.traffic), - items: [ - { - id: 'overview', - label: formatMessage(labels.overview), - icon: , - path: renderPath(''), - }, - { - id: 'events', - label: formatMessage(labels.events), - icon: , - path: renderPath('/events'), - }, - { - id: 'sessions', - label: formatMessage(labels.sessions), - icon: , - path: renderPath('/sessions'), - }, - { - id: 'realtime', - label: formatMessage(labels.realtime), - icon: , - path: renderPath('/realtime'), - }, - { - id: 'compare', - label: formatMessage(labels.compare), - icon: , - path: renderPath('/compare'), - }, - { - id: 'breakdown', - label: formatMessage(labels.breakdown), - icon: , - path: renderPath('/breakdown'), - }, - ], - }, - { - label: formatMessage(labels.behavior), - items: [ - { - id: 'goals', - label: formatMessage(labels.goals), - icon: , - path: renderPath('/goals'), - }, - { - id: 'funnel', - label: formatMessage(labels.funnels), - icon: , - path: renderPath('/funnels'), - }, - { - id: 'journeys', - label: formatMessage(labels.journeys), - icon: , - path: renderPath('/journeys'), - }, - { - id: 'retention', - label: formatMessage(labels.retention), - icon: , - path: renderPath('/retention'), - }, - ], - }, - { - label: formatMessage(labels.audience), - items: [ - { - id: 'segments', - label: formatMessage(labels.segments), - icon: , - path: renderPath('/segments'), - }, - { - id: 'cohorts', - label: formatMessage(labels.cohorts), - icon: , - path: renderPath('/cohorts'), - }, - ], - }, - { - label: formatMessage(labels.growth), - items: [ - { - id: 'utm', - label: formatMessage(labels.utm), - icon: , - path: renderPath('/utm'), - }, - { - id: 'revenue', - label: formatMessage(labels.revenue), - icon: , - path: renderPath('/revenue'), - }, - { - id: 'attribution', - label: formatMessage(labels.attribution), - icon: , - path: renderPath('/attribution'), - }, - ], - }, - ]; + const { teamId, router, renderUrl } = useNavigation(); + const { items, selectedKey } = useWebsiteNavItems(websiteId); const handleChange = (value: string) => { router.push(renderUrl(`/websites/${value}`)); @@ -158,10 +25,6 @@ export function WebsiteNav({ ); }; - const selectedKey = items - .flatMap(e => e.items) - .find(({ path }) => path && pathname.endsWith(path.split('?')[0]))?.id; - return ( + renderUrl(`/websites/${websiteId}${path}`, { + event: undefined, + compare: undefined, + view: undefined, + unit: undefined, + excludeBounce: undefined, + }); + + const items = [ + { + label: formatMessage(labels.traffic), + items: [ + { + id: 'overview', + label: formatMessage(labels.overview), + icon: , + path: renderPath(''), + }, + { + id: 'events', + label: formatMessage(labels.events), + icon: , + path: renderPath('/events'), + }, + { + id: 'sessions', + label: formatMessage(labels.sessions), + icon: , + path: renderPath('/sessions'), + }, + { + id: 'realtime', + label: formatMessage(labels.realtime), + icon: , + path: renderPath('/realtime'), + }, + { + id: 'compare', + label: formatMessage(labels.compare), + icon: , + path: renderPath('/compare'), + }, + { + id: 'breakdown', + label: formatMessage(labels.breakdown), + icon: , + path: renderPath('/breakdown'), + }, + ], + }, + { + label: formatMessage(labels.behavior), + items: [ + { + id: 'goals', + label: formatMessage(labels.goals), + icon: , + path: renderPath('/goals'), + }, + { + id: 'funnel', + label: formatMessage(labels.funnels), + icon: , + path: renderPath('/funnels'), + }, + { + id: 'journeys', + label: formatMessage(labels.journeys), + icon: , + path: renderPath('/journeys'), + }, + { + id: 'retention', + label: formatMessage(labels.retention), + icon: , + path: renderPath('/retention'), + }, + ], + }, + { + label: formatMessage(labels.audience), + items: [ + { + id: 'segments', + label: formatMessage(labels.segments), + icon: , + path: renderPath('/segments'), + }, + { + id: 'cohorts', + label: formatMessage(labels.cohorts), + icon: , + path: renderPath('/cohorts'), + }, + ], + }, + { + label: formatMessage(labels.growth), + items: [ + { + id: 'utm', + label: formatMessage(labels.utm), + icon: , + path: renderPath('/utm'), + }, + { + id: 'revenue', + label: formatMessage(labels.revenue), + icon: , + path: renderPath('/revenue'), + }, + { + id: 'attribution', + label: formatMessage(labels.attribution), + icon: , + path: renderPath('/attribution'), + }, + ], + }, + ]; + + const selectedKey = items + .flatMap(e => e.items) + .find(({ path }) => path && pathname.endsWith(path.split('?')[0]))?.id; + + return { items, selectedKey, renderPath }; +}