From 4e25c071604a3d7fb9fecc32e4e2d3dd1fe0275f Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Thu, 7 Aug 2025 22:10:02 -0700 Subject: [PATCH] Implemented expanded view in dialog. --- .../[websiteId]/WebsiteCompareTables.tsx | 51 ++++++----- .../[websiteId]/WebsiteDetailsPage.tsx | 26 +++++- .../[websiteId]/WebsiteExpandedView.tsx | 91 +++++++++---------- .../websites/[websiteId]/WebsiteTableView.tsx | 14 +-- .../[websiteId]/segments/SegmentEditForm.tsx | 2 +- src/components/common/SideMenu.tsx | 13 ++- src/components/hooks/useNavigation.ts | 8 +- src/components/metrics/MetricsTable.tsx | 33 ++++--- .../metrics/QueryParametersTable.tsx | 2 +- 9 files changed, 136 insertions(+), 104 deletions(-) diff --git a/src/app/(main)/websites/[websiteId]/WebsiteCompareTables.tsx b/src/app/(main)/websites/[websiteId]/WebsiteCompareTables.tsx index f173717c..90386a3a 100644 --- a/src/app/(main)/websites/[websiteId]/WebsiteCompareTables.tsx +++ b/src/app/(main)/websites/[websiteId]/WebsiteCompareTables.tsx @@ -1,6 +1,5 @@ -import { Grid, Heading, Column, Row } from '@umami/react-zen'; +import { Grid, Heading, Column, Row, NavMenu, NavMenuItem, Text } from '@umami/react-zen'; import { useDateRange, useMessages, useNavigation } from '@/components/hooks'; -import { SideMenu } from '@/components/common/SideMenu'; import { BrowsersTable } from '@/components/metrics/BrowsersTable'; import { ChangeLabel } from '@/components/metrics/ChangeLabel'; import { CitiesTable } from '@/components/metrics/CitiesTable'; @@ -40,7 +39,7 @@ const views = { }; export function WebsiteCompareTables({ websiteId }: { websiteId: string }) { - const [data, setData] = useState([]); + const [data] = useState([]); const { dateRange, dateCompare } = useDateRange(websiteId); const { formatMessage, labels } = useMessages(); const { @@ -53,72 +52,72 @@ export function WebsiteCompareTables({ websiteId }: { websiteId: string }) { { id: 'path', label: formatMessage(labels.pages), - url: updateParams({ view: 'path' }), + path: updateParams({ view: 'path' }), }, { id: 'referrer', label: formatMessage(labels.referrers), - url: updateParams({ view: 'referrer' }), + path: updateParams({ view: 'referrer' }), }, { id: 'browser', label: formatMessage(labels.browsers), - url: updateParams({ view: 'browser' }), + path: updateParams({ view: 'browser' }), }, { id: 'os', label: formatMessage(labels.os), - url: updateParams({ view: 'os' }), + path: updateParams({ view: 'os' }), }, { id: 'device', label: formatMessage(labels.devices), - url: updateParams({ view: 'device' }), + path: updateParams({ view: 'device' }), }, { id: 'country', label: formatMessage(labels.countries), - url: updateParams({ view: 'country' }), + path: updateParams({ view: 'country' }), }, { id: 'region', label: formatMessage(labels.regions), - url: updateParams({ view: 'region' }), + path: updateParams({ view: 'region' }), }, { id: 'city', label: formatMessage(labels.cities), - url: updateParams({ view: 'city' }), + path: updateParams({ view: 'city' }), }, { id: 'language', label: formatMessage(labels.languages), - url: updateParams({ view: 'language' }), + path: updateParams({ view: 'language' }), }, { id: 'screen', label: formatMessage(labels.screens), - url: updateParams({ view: 'screen' }), + path: updateParams({ view: 'screen' }), }, { id: 'event', label: formatMessage(labels.events), - url: updateParams({ view: 'event' }), + path: updateParams({ view: 'event' }), }, { id: 'query', label: formatMessage(labels.queryParameters), - url: updateParams({ view: 'query' }), + path: updateParams({ view: 'query' }), }, { id: 'hostname', label: formatMessage(labels.hostname), - url: updateParams({ view: 'hostname' }), + path: updateParams({ view: 'hostname' }), }, { id: 'tag', label: formatMessage(labels.tags), - url: updateParams({ view: 'tag' }), + path: updateParams({ view: 'tag' }), }, ]; @@ -150,19 +149,21 @@ export function WebsiteCompareTables({ websiteId }: { websiteId: string }) { return ( - + + {items.map(({ id, label }) => { + return ( + + {label} + + ); + })} + {formatMessage(labels.previous)} - + diff --git a/src/app/(main)/websites/[websiteId]/WebsiteDetailsPage.tsx b/src/app/(main)/websites/[websiteId]/WebsiteDetailsPage.tsx index bdf7ef74..99517bb5 100644 --- a/src/app/(main)/websites/[websiteId]/WebsiteDetailsPage.tsx +++ b/src/app/(main)/websites/[websiteId]/WebsiteDetailsPage.tsx @@ -1,18 +1,29 @@ 'use client'; -import { Column } from '@umami/react-zen'; +import { Column, Modal, Dialog } from '@umami/react-zen'; import { useNavigation } from '@/components/hooks'; import { Panel } from '@/components/common/Panel'; import { WebsiteChart } from './WebsiteChart'; import { WebsiteExpandedView } from './WebsiteExpandedView'; import { WebsiteMetricsBar } from './WebsiteMetricsBar'; import { WebsiteTableView } from './WebsiteTableView'; -import { WebsiteCompareTables } from './WebsiteCompareTables'; import { WebsiteControls } from './WebsiteControls'; export function WebsiteDetailsPage({ websiteId }: { websiteId: string }) { const { + router, query: { view, compare }, + updateParams, } = useNavigation(); + const handleClose = (close: () => void) => { + router.push(updateParams({ view: undefined })); + close(); + }; + + const handleOpenChange = (isOpen: boolean) => { + if (!isOpen) { + router.push(updateParams({ view: undefined })); + } + }; return ( @@ -21,9 +32,14 @@ export function WebsiteDetailsPage({ websiteId }: { websiteId: string }) { - {!view && !compare && } - {view && !compare && } - {compare && } + + + + {({ close }) => { + return handleClose(close)} />; + }} + + ); } diff --git a/src/app/(main)/websites/[websiteId]/WebsiteExpandedView.tsx b/src/app/(main)/websites/[websiteId]/WebsiteExpandedView.tsx index e6788ae9..dabb2f49 100644 --- a/src/app/(main)/websites/[websiteId]/WebsiteExpandedView.tsx +++ b/src/app/(main)/websites/[websiteId]/WebsiteExpandedView.tsx @@ -1,7 +1,5 @@ -import { Icon, Text, Grid, Column } from '@umami/react-zen'; -import { LinkButton } from '@/components/common/LinkButton'; +import { Grid, Column, NavMenu, NavMenuItem } from '@umami/react-zen'; import { useMessages, useNavigation } from '@/components/hooks'; -import { SideMenu } from '@/components/common/SideMenu'; import { BrowsersTable } from '@/components/metrics/BrowsersTable'; import { CitiesTable } from '@/components/metrics/CitiesTable'; import { CountriesTable } from '@/components/metrics/CountriesTable'; @@ -17,8 +15,7 @@ import { RegionsTable } from '@/components/metrics/RegionsTable'; import { ScreenTable } from '@/components/metrics/ScreenTable'; import { TagsTable } from '@/components/metrics/TagsTable'; import { ChannelsTable } from '@/components/metrics/ChannelsTable'; -import { Panel } from '@/components/common/Panel'; -import { Arrow } from '@/components/icons'; +import Link from 'next/link'; const views = { path: PagesTable, @@ -44,10 +41,10 @@ const views = { export function WebsiteExpandedView({ websiteId, - domainName, + onClose, }: { websiteId: string; - domainName?: string; + onClose?: () => void; }) { const { formatMessage, labels } = useMessages(); const { @@ -59,107 +56,107 @@ export function WebsiteExpandedView({ { id: 'path', label: formatMessage(labels.pages), - url: updateParams({ view: 'path' }), + path: updateParams({ view: 'path' }), }, { id: 'referrer', label: formatMessage(labels.referrers), - url: updateParams({ view: 'referrer' }), + path: updateParams({ view: 'referrer' }), }, { id: 'channel', label: formatMessage(labels.channels), - url: updateParams({ view: 'channel' }), + path: updateParams({ view: 'channel' }), }, { id: 'browser', label: formatMessage(labels.browsers), - url: updateParams({ view: 'browser' }), + path: updateParams({ view: 'browser' }), }, { id: 'os', label: formatMessage(labels.os), - url: updateParams({ view: 'os' }), + path: updateParams({ view: 'os' }), }, { id: 'device', label: formatMessage(labels.devices), - url: updateParams({ view: 'device' }), + path: updateParams({ view: 'device' }), }, { id: 'country', label: formatMessage(labels.countries), - url: updateParams({ view: 'country' }), + path: updateParams({ view: 'country' }), }, { id: 'region', label: formatMessage(labels.regions), - url: updateParams({ view: 'region' }), + path: updateParams({ view: 'region' }), }, { id: 'city', label: formatMessage(labels.cities), - url: updateParams({ view: 'city' }), + path: updateParams({ view: 'city' }), }, { id: 'language', label: formatMessage(labels.languages), - url: updateParams({ view: 'language' }), + path: updateParams({ view: 'language' }), }, { id: 'screen', label: formatMessage(labels.screens), - url: updateParams({ view: 'screen' }), + path: updateParams({ view: 'screen' }), }, { id: 'event', label: formatMessage(labels.events), - url: updateParams({ view: 'event' }), + path: updateParams({ view: 'event' }), }, { id: 'query', label: formatMessage(labels.queryParameters), - url: updateParams({ view: 'query' }), + path: updateParams({ view: 'query' }), }, { id: 'hostname', label: formatMessage(labels.hostname), - url: updateParams({ view: 'hostname' }), + path: updateParams({ view: 'hostname' }), }, { id: 'tag', label: formatMessage(labels.tags), - url: updateParams({ view: 'tag' }), + path: updateParams({ view: 'tag' }), }, ]; const DetailsComponent = views[view] || (() => null); return ( - - - - - - - - {formatMessage(labels.back)} - - - - - - - - + + + + {items.map(({ id, label, path }) => { + return ( + + {label} + + ); + })} + + + + + + ); } diff --git a/src/app/(main)/websites/[websiteId]/WebsiteTableView.tsx b/src/app/(main)/websites/[websiteId]/WebsiteTableView.tsx index 1eb19744..93567cc1 100644 --- a/src/app/(main)/websites/[websiteId]/WebsiteTableView.tsx +++ b/src/app/(main)/websites/[websiteId]/WebsiteTableView.tsx @@ -10,25 +10,27 @@ import { WorldMap } from '@/components/metrics/WorldMap'; import { CountriesTable } from '@/components/metrics/CountriesTable'; export function WebsiteTableView({ websiteId }: { websiteId: string }) { + const props = { websiteId, limit: 10, allowDownload: false }; + return ( - + - + - + - + - + @@ -36,7 +38,7 @@ export function WebsiteTableView({ websiteId }: { websiteId: string }) { - + diff --git a/src/app/(main)/websites/[websiteId]/segments/SegmentEditForm.tsx b/src/app/(main)/websites/[websiteId]/segments/SegmentEditForm.tsx index b19785a5..c93b1543 100644 --- a/src/app/(main)/websites/[websiteId]/segments/SegmentEditForm.tsx +++ b/src/app/(main)/websites/[websiteId]/segments/SegmentEditForm.tsx @@ -22,7 +22,7 @@ export function SegmentEditForm({ onSave, onClose, }: { - segmentId: string; + segmentId?: string; websiteId: string; filters?: any[]; showFilters?: boolean; diff --git a/src/components/common/SideMenu.tsx b/src/components/common/SideMenu.tsx index 4daa4909..2ded54f8 100644 --- a/src/components/common/SideMenu.tsx +++ b/src/components/common/SideMenu.tsx @@ -15,9 +15,16 @@ export interface SideMenuProps { title?: string; selectedKey?: string; allowMinimize?: boolean; + children?: React.ReactNode; } -export function SideMenu({ items, title, selectedKey, allowMinimize, children }: SideMenuProps) { +export function SideMenu({ + items = [], + title, + selectedKey, + allowMinimize, + children, +}: SideMenuProps) { return ( )} - {items.map(({ label, items }) => { + {items?.map(({ label, items }) => { return ( - {items.map(({ id, label, icon, path }) => { + {items?.map(({ id, label, icon, path }) => { const isSelected = selectedKey === id; return ( diff --git a/src/components/hooks/useNavigation.ts b/src/components/hooks/useNavigation.ts index 4db2dfe9..cdfb1fc6 100644 --- a/src/components/hooks/useNavigation.ts +++ b/src/components/hooks/useNavigation.ts @@ -18,10 +18,6 @@ export function useNavigation() { return buildUrl(pathname, params); }; - useEffect(() => { - setQueryParams(Object.fromEntries(searchParams)); - }, [searchParams.toString()]); - const renderUrl = (path: string, params?: Record | false) => { return buildUrl( teamId ? `/teams/${teamId}${path}` : path, @@ -29,6 +25,10 @@ export function useNavigation() { ); }; + useEffect(() => { + setQueryParams(Object.fromEntries(searchParams)); + }, [searchParams.toString()]); + return { router, pathname, diff --git a/src/components/metrics/MetricsTable.tsx b/src/components/metrics/MetricsTable.tsx index c18e46e4..8bac8f93 100644 --- a/src/components/metrics/MetricsTable.tsx +++ b/src/components/metrics/MetricsTable.tsx @@ -7,11 +7,11 @@ import { useWebsiteExpandedMetricsQuery, useWebsiteMetricsQuery, } from '@/components/hooks'; -import { Arrow } from '@/components/icons'; +import { Close, Maximize } from '@/components/icons'; import { DownloadButton } from '@/components/input/DownloadButton'; import { DEFAULT_ANIMATION_DURATION } from '@/lib/constants'; import { percentFilter } from '@/lib/filters'; -import { Column, Icon, Row, SearchField, Text } from '@umami/react-zen'; +import { Button, Column, Icon, Row, SearchField, Text } from '@umami/react-zen'; import { ReactNode, useMemo, useState } from 'react'; import { ListExpandedTable, ListExpandedTableProps } from './ListExpandedTable'; import { ListTable, ListTableProps } from './ListTable'; @@ -28,7 +28,8 @@ export interface MetricsTableProps extends ListTableProps { showMore?: boolean; params?: { [key: string]: any }; allowDownload?: boolean; - expanded?: boolean; + isExpanded?: boolean; + onClose?: () => void; children?: ReactNode; } @@ -43,7 +44,8 @@ export function MetricsTable({ showMore = true, params, allowDownload = true, - expanded = false, + isExpanded = false, + onClose, children, ...props }: MetricsTableProps) { @@ -61,7 +63,7 @@ export function MetricsTable({ }, { retryDelay: delay || DEFAULT_ANIMATION_DURATION, - enabled: expanded, + enabled: isExpanded, }, ); @@ -75,11 +77,11 @@ export function MetricsTable({ }, { retryDelay: delay || DEFAULT_ANIMATION_DURATION, - enabled: !expanded, + enabled: !isExpanded, }, ); - const { data, isLoading, isFetching, error } = expanded ? expandedQuery : query; + const { data, isLoading, isFetching, error } = isExpanded ? expandedQuery : query; const filteredData = useMemo(() => { if (data) { @@ -110,20 +112,27 @@ export function MetricsTable({ return []; }, [data, dataFilter, search, limit, formatValue, type]); - const downloadData = expanded ? data : filteredData; + const downloadData = isExpanded ? data : filteredData; return ( {allowSearch && } - + {children} {allowDownload && } + {onClose && ( + + )} {data && - (expanded ? ( + (isExpanded ? ( ) : ( @@ -131,10 +140,10 @@ export function MetricsTable({ {showMore && data && !error && limit && ( - {formatMessage(labels.more)} - + + {formatMessage(labels.more)} )} diff --git a/src/components/metrics/QueryParametersTable.tsx b/src/components/metrics/QueryParametersTable.tsx index 0d3ac268..90fc42a6 100644 --- a/src/components/metrics/QueryParametersTable.tsx +++ b/src/components/metrics/QueryParametersTable.tsx @@ -58,7 +58,7 @@ export function QueryParametersTable({ dataFilter={filters[filter]} renderLabel={renderLabel} delay={0} - expanded={false} + isExpanded={false} > {allowFilter && }