From 13e11ee37148dfa5936ea3eda5f84ab4e54dd106 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Sat, 25 May 2024 09:31:38 -0700 Subject: [PATCH] Added tables to compare screen. --- .../websites/[websiteId]/WebsiteChart.tsx | 10 +- ...siteDetails.tsx => WebsiteDetailsPage.tsx} | 25 +--- .../websites/[websiteId]/WebsiteTableView.tsx | 9 +- .../compare/WebsiteComparePage.tsx | 7 +- .../compare/WebsiteCompareTables.module.css | 7 + .../compare/WebsiteCompareTables.tsx | 137 ++++++++++++++++++ src/app/(main)/websites/[websiteId]/page.tsx | 4 +- src/app/share/[...shareId]/SharePage.tsx | 4 +- src/components/charts/Chart.tsx | 4 +- src/components/input/WebsiteDateFilter.tsx | 18 +-- src/components/layout/Grid.module.css | 4 + src/components/layout/Grid.tsx | 5 +- src/components/metrics/ListTable.tsx | 14 +- src/components/metrics/MetricsTable.tsx | 5 +- src/components/metrics/PagesTable.tsx | 12 +- 15 files changed, 199 insertions(+), 66 deletions(-) rename src/app/(main)/websites/[websiteId]/{WebsiteDetails.tsx => WebsiteDetailsPage.tsx} (58%) create mode 100644 src/app/(main)/websites/[websiteId]/compare/WebsiteCompareTables.module.css create mode 100644 src/app/(main)/websites/[websiteId]/compare/WebsiteCompareTables.tsx diff --git a/src/app/(main)/websites/[websiteId]/WebsiteChart.tsx b/src/app/(main)/websites/[websiteId]/WebsiteChart.tsx index d36cdad6..48da2377 100644 --- a/src/app/(main)/websites/[websiteId]/WebsiteChart.tsx +++ b/src/app/(main)/websites/[websiteId]/WebsiteChart.tsx @@ -9,7 +9,7 @@ export function WebsiteChart({ compareMode = false, }: { websiteId: string; - compareMode: boolean; + compareMode?: boolean; }) { const { dateRange, dateCompare } = useDateRange(websiteId); const { startDate, endDate, unit } = dateRange; @@ -27,13 +27,13 @@ export function WebsiteChart({ result['compare'] = { pageviews: result.pageviews.map(({ x }, i) => ({ x, - y: compare.pageviews[i].y, - d: compare.pageviews[i].x, + y: compare.pageviews[i]?.y, + d: compare.pageviews[i]?.x, })), sessions: result.sessions.map(({ x }, i) => ({ x, - y: compare.sessions[i].y, - d: compare.pageviews[i].x, + y: compare.sessions[i]?.y, + d: compare.sessions[i]?.x, })), }; } diff --git a/src/app/(main)/websites/[websiteId]/WebsiteDetails.tsx b/src/app/(main)/websites/[websiteId]/WebsiteDetailsPage.tsx similarity index 58% rename from src/app/(main)/websites/[websiteId]/WebsiteDetails.tsx rename to src/app/(main)/websites/[websiteId]/WebsiteDetailsPage.tsx index 1fd6992f..2988494e 100644 --- a/src/app/(main)/websites/[websiteId]/WebsiteDetails.tsx +++ b/src/app/(main)/websites/[websiteId]/WebsiteDetailsPage.tsx @@ -1,25 +1,19 @@ 'use client'; -import { Loading } from 'react-basics'; import { usePathname } from 'next/navigation'; -import Page from 'components/layout/Page'; import FilterTags from 'components/metrics/FilterTags'; -import { useNavigation, useWebsite } from 'components/hooks'; +import { useNavigation } from 'components/hooks'; import WebsiteChart from './WebsiteChart'; import WebsiteExpandedView from './WebsiteExpandedView'; import WebsiteHeader from './WebsiteHeader'; import WebsiteMetricsBar from './WebsiteMetricsBar'; import WebsiteTableView from './WebsiteTableView'; +import WebsiteProvider from './WebsiteProvider'; import { FILTER_COLUMNS } from 'lib/constants'; -export default function WebsiteDetails({ websiteId }: { websiteId: string }) { - const { data: website, isLoading, error } = useWebsite(websiteId); +export default function WebsiteDetailsPage({ websiteId }: { websiteId: string }) { const pathname = usePathname(); const { query } = useNavigation(); - if (isLoading || error) { - return ; - } - const showLinks = !pathname.includes('/share/'); const { view } = query; @@ -31,18 +25,13 @@ export default function WebsiteDetails({ websiteId }: { websiteId: string }) { }, {}); return ( - <> + - {!website && } - {website && ( - <> - {!view && } - {view && } - - )} - + {!view && } + {view && } + ); } diff --git a/src/app/(main)/websites/[websiteId]/WebsiteTableView.tsx b/src/app/(main)/websites/[websiteId]/WebsiteTableView.tsx index 7cc415e5..e530f2ba 100644 --- a/src/app/(main)/websites/[websiteId]/WebsiteTableView.tsx +++ b/src/app/(main)/websites/[websiteId]/WebsiteTableView.tsx @@ -10,17 +10,10 @@ import CountriesTable from 'components/metrics/CountriesTable'; import EventsTable from 'components/metrics/EventsTable'; import EventsChart from 'components/metrics/EventsChart'; -export default function WebsiteTableView({ - websiteId, - domainName, -}: { - websiteId: string; - domainName: string; -}) { +export default function WebsiteTableView({ websiteId }: { websiteId: string }) { const [countryData, setCountryData] = useState(); const tableProps = { websiteId, - domainName, limit: 10, }; diff --git a/src/app/(main)/websites/[websiteId]/compare/WebsiteComparePage.tsx b/src/app/(main)/websites/[websiteId]/compare/WebsiteComparePage.tsx index da330691..092ebe94 100644 --- a/src/app/(main)/websites/[websiteId]/compare/WebsiteComparePage.tsx +++ b/src/app/(main)/websites/[websiteId]/compare/WebsiteComparePage.tsx @@ -5,6 +5,8 @@ import FilterTags from 'components/metrics/FilterTags'; import { useNavigation } from 'components/hooks'; import { FILTER_COLUMNS } from 'lib/constants'; import WebsiteChart from '../WebsiteChart'; +import WebsiteCompareTables from './WebsiteCompareTables'; +import WebsiteProvider from '../WebsiteProvider'; export function WebsiteComparePage({ websiteId }) { const { query } = useNavigation(); @@ -17,12 +19,13 @@ export function WebsiteComparePage({ websiteId }) { }, {}); return ( - <> + - + + ); } diff --git a/src/app/(main)/websites/[websiteId]/compare/WebsiteCompareTables.module.css b/src/app/(main)/websites/[websiteId]/compare/WebsiteCompareTables.module.css new file mode 100644 index 00000000..4595f573 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/compare/WebsiteCompareTables.module.css @@ -0,0 +1,7 @@ +.container { + margin-bottom: 60px; +} + +.nav { + width: 200px; +} diff --git a/src/app/(main)/websites/[websiteId]/compare/WebsiteCompareTables.tsx b/src/app/(main)/websites/[websiteId]/compare/WebsiteCompareTables.tsx new file mode 100644 index 00000000..f5819770 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/compare/WebsiteCompareTables.tsx @@ -0,0 +1,137 @@ +import SideNav from 'components/layout/SideNav'; +import { useMessages, useNavigation } from 'components/hooks'; +import PagesTable from 'components/metrics/PagesTable'; +import ReferrersTable from 'components/metrics/ReferrersTable'; +import BrowsersTable from 'components/metrics/BrowsersTable'; +import OSTable from 'components/metrics/OSTable'; +import DevicesTable from 'components/metrics/DevicesTable'; +import ScreenTable from 'components/metrics/ScreenTable'; +import CountriesTable from 'components/metrics/CountriesTable'; +import RegionsTable from 'components/metrics/RegionsTable'; +import CitiesTable from 'components/metrics/CitiesTable'; +import LanguagesTable from 'components/metrics/LanguagesTable'; +import EventsTable from 'components/metrics/EventsTable'; +import QueryParametersTable from 'components/metrics/QueryParametersTable'; +import { Grid, GridRow } from 'components/layout/Grid'; +import styles from './WebsiteCompareTables.module.css'; +import { useContext, useState } from 'react'; +import MetricsTable from 'components/metrics/MetricsTable'; +import FilterLink from 'components/common/FilterLink'; +import { WebsiteContext } from '../WebsiteProvider'; + +const views = { + url: PagesTable, + title: PagesTable, + referrer: ReferrersTable, + browser: BrowsersTable, + os: OSTable, + device: DevicesTable, + screen: ScreenTable, + country: CountriesTable, + region: RegionsTable, + city: CitiesTable, + language: LanguagesTable, + event: EventsTable, + query: QueryParametersTable, +}; + +export function WebsiteCompareTables({ websiteId }: { websiteId: string }) { + const { domain } = useContext(WebsiteContext); + const [data, setData] = useState([]); + const { formatMessage, labels } = useMessages(); + const { + renderUrl, + query: { view }, + } = useNavigation(); + const Component: typeof MetricsTable = views[view] || (() => null); + + const items = [ + { + key: 'url', + label: formatMessage(labels.pages), + url: renderUrl({ view: 'url' }), + }, + { + key: 'referrer', + label: formatMessage(labels.referrers), + url: renderUrl({ view: 'referrer' }), + }, + { + key: 'browser', + label: formatMessage(labels.browsers), + url: renderUrl({ view: 'browser' }), + }, + { + key: 'os', + label: formatMessage(labels.os), + url: renderUrl({ view: 'os' }), + }, + { + key: 'device', + label: formatMessage(labels.devices), + url: renderUrl({ view: 'device' }), + }, + { + key: 'country', + label: formatMessage(labels.countries), + url: renderUrl({ view: 'country' }), + }, + { + key: 'region', + label: formatMessage(labels.regions), + url: renderUrl({ view: 'region' }), + }, + { + key: 'city', + label: formatMessage(labels.cities), + url: renderUrl({ view: 'city' }), + }, + { + key: 'language', + label: formatMessage(labels.languages), + url: renderUrl({ view: 'language' }), + }, + { + key: 'screen', + label: formatMessage(labels.screens), + url: renderUrl({ view: 'screen' }), + }, + { + key: 'event', + label: formatMessage(labels.events), + url: renderUrl({ view: 'event' }), + }, + { + key: 'query', + label: formatMessage(labels.queryParameters), + url: renderUrl({ view: 'query' }), + }, + ]; + + const renderLabel = ({ x, y }, index) => { + return ( + + {y} : {data[index]?.y} ! + + ); + }; + + return ( + + + + + + + + ); +} + +export default WebsiteCompareTables; diff --git a/src/app/(main)/websites/[websiteId]/page.tsx b/src/app/(main)/websites/[websiteId]/page.tsx index ddb6c833..49bca9b6 100644 --- a/src/app/(main)/websites/[websiteId]/page.tsx +++ b/src/app/(main)/websites/[websiteId]/page.tsx @@ -1,8 +1,8 @@ -import WebsiteDetails from './WebsiteDetails'; +import WebsiteDetailsPage from './WebsiteDetailsPage'; import { Metadata } from 'next'; export default function WebsitePage({ params: { websiteId } }) { - return ; + return ; } export const metadata: Metadata = { diff --git a/src/app/share/[...shareId]/SharePage.tsx b/src/app/share/[...shareId]/SharePage.tsx index b49d36ec..4ac6af37 100644 --- a/src/app/share/[...shareId]/SharePage.tsx +++ b/src/app/share/[...shareId]/SharePage.tsx @@ -1,5 +1,5 @@ 'use client'; -import WebsiteDetails from 'app/(main)/websites/[websiteId]/WebsiteDetails'; +import WebsiteDetailsPage from '../../(main)/websites/[websiteId]/WebsiteDetailsPage'; import { useShareToken } from 'components/hooks'; import Page from 'components/layout/Page'; import Header from './Header'; @@ -17,7 +17,7 @@ export default function SharePage({ shareId }) {
- +
diff --git a/src/components/charts/Chart.tsx b/src/components/charts/Chart.tsx index 47d29c07..6ba60159 100644 --- a/src/components/charts/Chart.tsx +++ b/src/components/charts/Chart.tsx @@ -98,9 +98,9 @@ export function Chart({ // Allow config changes before update onUpdate?.(chart.current); - setLegendItems(chart.current.legend.legendItems); - chart.current.update(updateMode); + + setLegendItems(chart.current.legend.legendItems); }; useEffect(() => { diff --git a/src/components/input/WebsiteDateFilter.tsx b/src/components/input/WebsiteDateFilter.tsx index 12718b3d..4f2dee0a 100644 --- a/src/components/input/WebsiteDateFilter.tsx +++ b/src/components/input/WebsiteDateFilter.tsx @@ -23,6 +23,15 @@ export function WebsiteDateFilter({ websiteId }: { websiteId: string }) { return (
+ {value !== 'all' && !value.startsWith('range') && (
)} -
); } diff --git a/src/components/layout/Grid.module.css b/src/components/layout/Grid.module.css index f72a5f12..de99b752 100644 --- a/src/components/layout/Grid.module.css +++ b/src/components/layout/Grid.module.css @@ -8,6 +8,10 @@ border-top: 1px solid var(--base300); } +.row.compare { + grid-template-columns: max-content 1fr 1fr; +} + .col { padding: 20px; min-height: 430px; diff --git a/src/components/layout/Grid.tsx b/src/components/layout/Grid.tsx index 2a34fdc4..ec7f4fda 100644 --- a/src/components/layout/Grid.tsx +++ b/src/components/layout/Grid.tsx @@ -1,6 +1,7 @@ import { CSSProperties } from 'react'; import classNames from 'classnames'; import { mapChildren } from 'react-basics'; +// eslint-disable-next-line css-modules/no-unused-class import styles from './Grid.module.css'; export interface GridProps { @@ -19,13 +20,13 @@ export function Grid({ className, style, children }: GridProps) { export function GridRow(props: { [x: string]: any; - columns?: 'one' | 'two' | 'three' | 'one-two' | 'two-one'; + columns?: 'one' | 'two' | 'three' | 'one-two' | 'two-one' | 'compare'; className?: string; children?: any; }) { const { columns = 'two', className, children, ...otherProps } = props; return ( -
+
{mapChildren(children, child => { return
{child}
; })} diff --git a/src/components/metrics/ListTable.tsx b/src/components/metrics/ListTable.tsx index 133905e1..14f2c990 100644 --- a/src/components/metrics/ListTable.tsx +++ b/src/components/metrics/ListTable.tsx @@ -14,7 +14,7 @@ export interface ListTableProps { title?: string; metric?: string; className?: string; - renderLabel?: (row: any) => ReactNode; + renderLabel?: (row: any, index: number) => ReactNode; animate?: boolean; virtualize?: boolean; showPercentage?: boolean; @@ -34,13 +34,13 @@ export function ListTable({ }: ListTableProps) { const { formatMessage, labels } = useMessages(); - const getRow = row => { + const getRow = (row: { x: any; y: any; z: any }, index: number) => { const { x: label, y: value, z: percent } = row; return ( { - return
{getRow(data[index])}
; + return
{getRow(data[index], index)}
; }; return ( @@ -71,7 +71,7 @@ export function ListTable({ {Row} ) : ( - data.map(row => getRow(row)) + data.map(getRow) )}
@@ -97,9 +97,7 @@ const AnimatedRow = ({ label, value = 0, percent, animate, showPercentage = true {showPercentage && (
`${n}%`) }} /> - - {props.width.to(n => `${n?.toFixed?.(0)}%`)} - + {props.width.to(n => `${n?.toFixed?.(0)}%`)}
)} diff --git a/src/components/metrics/MetricsTable.tsx b/src/components/metrics/MetricsTable.tsx index 857c136b..6da0e289 100644 --- a/src/components/metrics/MetricsTable.tsx +++ b/src/components/metrics/MetricsTable.tsx @@ -18,7 +18,6 @@ import styles from './MetricsTable.module.css'; export interface MetricsTableProps extends ListTableProps { websiteId: string; - domainName: string; type?: string; className?: string; dataFilter?: (data: any) => any; @@ -27,6 +26,7 @@ export interface MetricsTableProps extends ListTableProps { onDataLoad?: (data: any) => void; onSearch?: (search: string) => void; allowSearch?: boolean; + showMore?: boolean; children?: ReactNode; } @@ -39,6 +39,7 @@ export function MetricsTable({ onDataLoad, delay = null, allowSearch = false, + showMore = true, children, ...props }: MetricsTableProps) { @@ -98,7 +99,7 @@ export function MetricsTable({ )} {!data && isLoading && !isFetched && }
- {data && !error && limit && ( + {showMore && data && !error && limit && ( {formatMessage(labels.more)} diff --git a/src/components/metrics/PagesTable.tsx b/src/components/metrics/PagesTable.tsx index b0da8080..ca3f6be5 100644 --- a/src/components/metrics/PagesTable.tsx +++ b/src/components/metrics/PagesTable.tsx @@ -4,18 +4,21 @@ import MetricsTable, { MetricsTableProps } from './MetricsTable'; import { useMessages } from 'components/hooks'; import { useNavigation } from 'components/hooks'; import { emptyFilter } from 'lib/filters'; +import { useContext } from 'react'; +import { WebsiteContext } from 'app/(main)/websites/[websiteId]/WebsiteProvider'; export interface PagesTableProps extends MetricsTableProps { allowFilter?: boolean; } -export function PagesTable({ allowFilter, domainName, ...props }: PagesTableProps) { +export function PagesTable({ allowFilter, ...props }: PagesTableProps) { const { router, renderUrl, query: { view = 'url' }, } = useNavigation(); const { formatMessage, labels } = useMessages(); + const { domain } = useContext(WebsiteContext); const handleSelect = (key: any) => { router.push(renderUrl({ view: key }), { scroll: true }); @@ -39,9 +42,7 @@ export function PagesTable({ allowFilter, domainName, ...props }: PagesTableProp value={x} label={!x && formatMessage(labels.none)} externalUrl={ - view === 'url' - ? `${domainName.startsWith('http') ? domainName : `https://${domainName}`}${x}` - : null + view === 'url' ? `${domain.startsWith('http') ? domain : `https://${domain}`}${x}` : null } /> ); @@ -50,12 +51,11 @@ export function PagesTable({ allowFilter, domainName, ...props }: PagesTableProp return ( {allowFilter && }