From e4c5f4218999b3948fce643fdd86d9bbde01401a Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Mon, 2 Oct 2023 23:51:26 -0700 Subject: [PATCH] CSS updates. --- src/app/(app)/console/TestConsole.js | 28 ++--- src/app/(app)/websites/WebsiteTableView.js | 50 +++----- .../websites/WebsiteTableView.module.css | 35 ------ src/app/(app)/websites/[id]/WebsiteHeader.js | 18 +-- .../websites/[id]/WebsiteHeader.module.css | 12 +- .../(app)/websites/[id]/WebsiteMetricsBar.js | 114 ++++++++--------- .../[id]/WebsiteMetricsBar.module.css | 15 ++- .../[id]/event-data/EventDataMetricsBar.js | 41 ++---- .../event-data/EventDataMetricsBar.module.css | 30 +---- .../(app)/websites/[id]/realtime/Realtime.js | 20 ++- .../websites/[id]/realtime/RealtimePage.js | 117 ------------------ src/components/common/UpdateNotice.js | 14 +-- src/components/input/WebsiteDateFilter.js | 10 +- .../input/WebsiteDateFilter.module.css | 10 ++ src/components/layout/Grid.js | 21 ++-- src/components/layout/Grid.module.css | 48 +++++-- src/components/layout/Page.module.css | 1 - src/components/metrics/BarChart.js | 4 +- src/components/metrics/BarChart.module.css | 4 + src/components/metrics/MetricsBar.js | 11 +- src/components/metrics/MetricsBar.module.css | 17 +-- src/styles/index.css | 2 + 22 files changed, 227 insertions(+), 395 deletions(-) delete mode 100644 src/app/(app)/websites/WebsiteTableView.module.css delete mode 100644 src/app/(app)/websites/[id]/realtime/RealtimePage.js diff --git a/src/app/(app)/console/TestConsole.js b/src/app/(app)/console/TestConsole.js index ca7ffc4a..82edd191 100644 --- a/src/app/(app)/console/TestConsole.js +++ b/src/app/(app)/console/TestConsole.js @@ -9,7 +9,7 @@ import Head from 'next/head'; import Link from 'next/link'; import { useRouter } from 'next/navigation'; import Script from 'next/script'; -import { Button, Column, Row } from 'react-basics'; +import { Button } from 'react-basics'; import styles from './TestConsole.module.css'; export function TestConsole() { @@ -91,8 +91,8 @@ export function TestConsole() { src={`${basePath}/script.js`} data-cache="true" /> - - +
+
Page links
page one @@ -115,8 +115,8 @@ export function TestConsole() { external link (tab)
- - +
+
Click events
- - +
+
Javascript events
- - - - - - - - +
+
+
+ + +
)} diff --git a/src/app/(app)/websites/WebsiteTableView.js b/src/app/(app)/websites/WebsiteTableView.js index c46c8b73..7c71b84b 100644 --- a/src/app/(app)/websites/WebsiteTableView.js +++ b/src/app/(app)/websites/WebsiteTableView.js @@ -1,5 +1,5 @@ import { useState } from 'react'; -import { GridRow, GridColumn } from 'components/layout/Grid'; +import { Grid, GridRow } from 'components/layout/Grid'; import PagesTable from 'components/metrics/PagesTable'; import ReferrersTable from 'components/metrics/ReferrersTable'; import BrowsersTable from 'components/metrics/BrowsersTable'; @@ -18,42 +18,24 @@ export default function WebsiteTableView({ websiteId }) { }; return ( - <> - - - - - - - + + + + - - - - - - - - - - + + + + - - - - - - - + + + - - - - - - - + + + - + ); } diff --git a/src/app/(app)/websites/WebsiteTableView.module.css b/src/app/(app)/websites/WebsiteTableView.module.css deleted file mode 100644 index 73c5facb..00000000 --- a/src/app/(app)/websites/WebsiteTableView.module.css +++ /dev/null @@ -1,35 +0,0 @@ -.col { - display: flex; - flex-direction: column; -} - -.row { - border-top: 1px solid var(--base300); - min-height: 430px; -} - -.row > .col { - border-left: 1px solid var(--base300); - padding: 20px; -} - -.row > .col:first-child { - border-left: 0; - padding-left: 0; -} - -.row > .col:last-child { - padding-right: 0; -} - -@media only screen and (max-width: 992px) { - .row { - border: 0; - } - - .row > .col { - border-top: 1px solid var(--base300); - border-left: 0; - padding: 20px 0; - } -} diff --git a/src/app/(app)/websites/[id]/WebsiteHeader.js b/src/app/(app)/websites/[id]/WebsiteHeader.js index daedfd43..bf34a253 100644 --- a/src/app/(app)/websites/[id]/WebsiteHeader.js +++ b/src/app/(app)/websites/[id]/WebsiteHeader.js @@ -1,6 +1,6 @@ 'use client'; import classNames from 'classnames'; -import { Row, Column, Text, Button, Icon } from 'react-basics'; +import { Text, Button, Icon } from 'react-basics'; import Link from 'next/link'; import { usePathname } from 'next/navigation'; import Favicon from 'components/common/Favicon'; @@ -39,17 +39,19 @@ export function WebsiteHeader({ websiteId, showLinks = true, children }) { ]; return ( - - +
+
{name} - - +
+
{showLinks && (
{links.map(({ label, icon, path }) => { - const selected = path ? pathname.endsWith(path) : pathname === '/websites/[id]'; + const selected = path + ? pathname.endsWith(path) + : pathname.match(/^\/websites\/[\w-]+$/); return ( @@ -68,8 +70,8 @@ export function WebsiteHeader({ websiteId, showLinks = true, children }) {
)} {children} - - +
+
); } diff --git a/src/app/(app)/websites/[id]/WebsiteHeader.module.css b/src/app/(app)/websites/[id]/WebsiteHeader.module.css index 93e622d9..3e58c8a3 100644 --- a/src/app/(app)/websites/[id]/WebsiteHeader.module.css +++ b/src/app/(app)/websites/[id]/WebsiteHeader.module.css @@ -1,6 +1,6 @@ .header { - display: flex; - flex-direction: row; + display: grid; + grid-template-columns: 1fr max-content; align-items: center; } @@ -35,6 +35,10 @@ } @media only screen and (max-width: 768px) { + .header { + grid-template-columns: 1fr; + } + .links { justify-content: space-evenly; flex: 1; @@ -49,7 +53,7 @@ .icon, .icon svg { - width: 30px; - height: 30px; + width: 20px; + height: 20px; } } diff --git a/src/app/(app)/websites/[id]/WebsiteMetricsBar.js b/src/app/(app)/websites/[id]/WebsiteMetricsBar.js index 44165fb3..f0055eb6 100644 --- a/src/app/(app)/websites/[id]/WebsiteMetricsBar.js +++ b/src/app/(app)/websites/[id]/WebsiteMetricsBar.js @@ -6,7 +6,7 @@ import MetricsBar from 'components/metrics/MetricsBar'; import FilterSelectForm from '../../reports/FilterSelectForm'; import PopupForm from '../../reports/PopupForm'; import { formatShortTime } from 'lib/format'; -import { Button, Column, Icon, Icons, Popup, PopupTrigger, Row } from 'react-basics'; +import { Button, Icon, Icons, Popup, PopupTrigger } from 'react-basics'; import styles from './WebsiteMetricsBar.module.css'; export function WebsiteMetricsBar({ websiteId, showFilter = true, sticky }) { @@ -98,72 +98,64 @@ export function WebsiteMetricsBar({ websiteId, showFilter = true, sticky }) { }; return ( - - - - {!error && isFetched && ( - <> - - - Number(n).toFixed(0) + '%'} - reverseColors - /> - - `${n < 0 ? '-' : ''}${formatShortTime(Math.abs(~~n), ['m', 's'], ' ')}` - } - /> - - )} - - - -
- {showFilter && } - -
-
-
+ + {pageviews && uniques && ( + <> + + + Number(n).toFixed(0) + '%'} + reverseColors + /> + `${n < 0 ? '-' : ''}${formatShortTime(Math.abs(~~n), ['m', 's'], ' ')}`} + /> + + )} + +
+ {showFilter && } + +
+ ); } diff --git a/src/app/(app)/websites/[id]/WebsiteMetricsBar.module.css b/src/app/(app)/websites/[id]/WebsiteMetricsBar.module.css index 52decfc6..4d642d70 100644 --- a/src/app/(app)/websites/[id]/WebsiteMetricsBar.module.css +++ b/src/app/(app)/websites/[id]/WebsiteMetricsBar.module.css @@ -1,12 +1,12 @@ .container { - display: flex; + display: grid; + grid-template-columns: 1fr max-content; justify-content: space-between; align-items: center; - padding: 10px 0; - min-height: 90px; - margin-bottom: 20px; background: var(--base50); z-index: var(--z-index-above); + min-height: 120px; + padding-bottom: 20px; } .actions { @@ -18,8 +18,12 @@ } @media only screen and (max-width: 1200px) { + .container { + grid-template-columns: 1fr; + } + .actions { - margin-top: 40px; + margin: 20px 0; } } @@ -30,6 +34,7 @@ } .isSticky { + padding: 10px 0; border-bottom: 1px solid var(--base300); } } diff --git a/src/app/(app)/websites/[id]/event-data/EventDataMetricsBar.js b/src/app/(app)/websites/[id]/event-data/EventDataMetricsBar.js index 5d984bea..5be19185 100644 --- a/src/app/(app)/websites/[id]/event-data/EventDataMetricsBar.js +++ b/src/app/(app)/websites/[id]/event-data/EventDataMetricsBar.js @@ -1,4 +1,3 @@ -import { Column, Row } from 'react-basics'; import { useApi, useDateRange } from 'components/hooks'; import MetricCard from 'components/metrics/MetricCard'; import useMessages from 'components/hooks/useMessages'; @@ -23,36 +22,16 @@ export function EventDataMetricsBar({ websiteId }) { ); return ( - - - - {!error && isFetched && ( - <> - - - - - )} - - - -
- -
-
-
+
+ + + + + +
+ +
+
); } diff --git a/src/app/(app)/websites/[id]/event-data/EventDataMetricsBar.module.css b/src/app/(app)/websites/[id]/event-data/EventDataMetricsBar.module.css index 43b14580..408396c3 100644 --- a/src/app/(app)/websites/[id]/event-data/EventDataMetricsBar.module.css +++ b/src/app/(app)/websites/[id]/event-data/EventDataMetricsBar.module.css @@ -1,5 +1,6 @@ .container { - display: flex; + display: grid; + grid-template-columns: 1fr 1fr; justify-content: space-between; align-items: center; padding: 10px 0; @@ -9,12 +10,6 @@ z-index: var(--z-index-above); } -.metrics { - display: flex; - flex-direction: row; - align-items: center; -} - .actions { display: flex; flex-direction: row; @@ -23,24 +18,9 @@ flex: 1; } -.bar { - display: flex; - cursor: pointer; - min-height: 110px; - gap: 20px; - flex-wrap: wrap; -} - -.card { - justify-self: flex-start; -} - @media only screen and (max-width: 992px) { - .card { - flex-basis: calc(50% - 20px); + .container { + grid-template-columns: 1fr; + grid-template-rows: 1fr 1fr; } } - -.row { - border-bottom: 1px solid var(--border-color); -} diff --git a/src/app/(app)/websites/[id]/realtime/Realtime.js b/src/app/(app)/websites/[id]/realtime/Realtime.js index 0ec92f05..b4219b0a 100644 --- a/src/app/(app)/websites/[id]/realtime/Realtime.js +++ b/src/app/(app)/websites/[id]/realtime/Realtime.js @@ -2,7 +2,7 @@ import { useMemo, useState, useEffect } from 'react'; import { subMinutes, startOfMinute } from 'date-fns'; import firstBy from 'thenby'; -import { GridRow, GridColumn } from 'components/layout/Grid'; +import { Grid, GridRow } from 'components/layout/Grid'; import Page from 'components/layout/Page'; import RealtimeChart from 'components/metrics/RealtimeChart'; import WorldMap from 'components/common/WorldMap'; @@ -99,22 +99,16 @@ export function Realtime({ websiteId }) { - - + + - - - - - - + + - - - - +
+ ); } diff --git a/src/app/(app)/websites/[id]/realtime/RealtimePage.js b/src/app/(app)/websites/[id]/realtime/RealtimePage.js deleted file mode 100644 index 9363d0e7..00000000 --- a/src/app/(app)/websites/[id]/realtime/RealtimePage.js +++ /dev/null @@ -1,117 +0,0 @@ -import { useState, useEffect, useMemo } from 'react'; -import { subMinutes, startOfMinute } from 'date-fns'; -import firstBy from 'thenby'; -import { GridRow, GridColumn } from 'components/layout/Grid'; -import Page from 'components/layout/Page'; -import RealtimeChart from 'components/metrics/RealtimeChart'; -import WorldMap from 'components/common/WorldMap'; -import RealtimeLog from './RealtimeLog'; -import RealtimeHeader from './RealtimeHeader'; -import RealtimeUrls from './RealtimeUrls'; -import RealtimeCountries from './RealtimeCountries'; -import WebsiteHeader from '../WebsiteHeader'; -import useApi from 'components/hooks/useApi'; -import { percentFilter } from 'lib/filters'; -import { REALTIME_RANGE, REALTIME_INTERVAL } from 'lib/constants'; -import { useWebsite } from 'components/hooks'; -import styles from './Realtime.module.css'; - -function mergeData(state = [], data = [], time) { - const ids = state.map(({ __id }) => __id); - return state - .concat(data.filter(({ __id }) => !ids.includes(__id))) - .filter(({ timestamp }) => timestamp >= time); -} - -export function RealtimePage({ websiteId }) { - const [currentData, setCurrentData] = useState(); - const { get, useQuery } = useApi(); - const { data: website } = useWebsite(websiteId); - const { data, isLoading, error } = useQuery( - ['realtime', websiteId], - () => get(`/realtime/${websiteId}`, { startAt: currentData?.timestamp || 0 }), - { - enabled: !!(websiteId && website), - refetchInterval: REALTIME_INTERVAL, - cache: false, - }, - ); - - useEffect(() => { - if (data) { - const date = subMinutes(startOfMinute(new Date()), REALTIME_RANGE); - const time = date.getTime(); - - setCurrentData(state => ({ - pageviews: mergeData(state?.pageviews, data.pageviews, time), - sessions: mergeData(state?.sessions, data.sessions, time), - events: mergeData(state?.events, data.events, time), - timestamp: data.timestamp, - })); - } - }, [data]); - - const realtimeData = useMemo(() => { - if (!currentData) { - return { pageviews: [], sessions: [], events: [], countries: [], visitors: [] }; - } - - currentData.countries = percentFilter( - currentData.sessions - .reduce((arr, data) => { - if (!arr.find(({ id }) => id === data.id)) { - return arr.concat(data); - } - return arr; - }, []) - .reduce((arr, { country }) => { - if (country) { - const row = arr.find(({ x }) => x === country); - - if (!row) { - arr.push({ x: country, y: 1 }); - } else { - row.y += 1; - } - } - return arr; - }, []) - .sort(firstBy('y', -1)), - ); - - currentData.visitors = currentData.sessions.reduce((arr, val) => { - if (!arr.find(({ id }) => id === val.id)) { - return arr.concat(val); - } - return arr; - }, []); - - return currentData; - }, [currentData]); - - return ( - - - - - - - - - - - - - - - - - - - - - - ); -} - -export default RealtimePage; diff --git a/src/components/common/UpdateNotice.js b/src/components/common/UpdateNotice.js index 95eb350f..509df95c 100644 --- a/src/components/common/UpdateNotice.js +++ b/src/components/common/UpdateNotice.js @@ -1,7 +1,7 @@ 'use client'; import { useEffect, useCallback, useState } from 'react'; import { createPortal } from 'react-dom'; -import { Button, Row, Column } from 'react-basics'; +import { Button } from 'react-basics'; import { setItem } from 'next-basics'; import useStore, { checkVersion } from 'store/version'; import { REPO_URL, VERSION_CHECK } from 'lib/constants'; @@ -47,17 +47,17 @@ export function UpdateNotice({ user, config }) { } return createPortal( - - +
+
{formatMessage(messages.newVersionAvailable, { version: `v${latest}` })} - - +
+
- - , +
+
, document.body, ); } diff --git a/src/components/input/WebsiteDateFilter.js b/src/components/input/WebsiteDateFilter.js index 6903a708..1725ca3b 100644 --- a/src/components/input/WebsiteDateFilter.js +++ b/src/components/input/WebsiteDateFilter.js @@ -1,7 +1,7 @@ import useDateRange from 'components/hooks/useDateRange'; import { isAfter } from 'date-fns'; import { incrementDateRange } from 'lib/date'; -import { Button, Flexbox, Icon, Icons } from 'react-basics'; +import { Button, Icon, Icons } from 'react-basics'; import DateFilter from './DateFilter'; import styles from './WebsiteDateFilter.module.css'; @@ -22,9 +22,9 @@ export function WebsiteDateFilter({ websiteId }) { }; return ( - +
{value !== 'all' && selectedUnit && ( - +
- +
)} -
+
); } diff --git a/src/components/input/WebsiteDateFilter.module.css b/src/components/input/WebsiteDateFilter.module.css index 986f5c17..6f2e822d 100644 --- a/src/components/input/WebsiteDateFilter.module.css +++ b/src/components/input/WebsiteDateFilter.module.css @@ -1,7 +1,17 @@ +.container { + display: flex; + align-items: center; + gap: 10px; +} + .dropdown { min-width: 200px; } +.buttons { + display: flex; +} + .buttons button:first-child { border-top-right-radius: 0; border-bottom-right-radius: 0; diff --git a/src/components/layout/Grid.js b/src/components/layout/Grid.js index 0276063b..080310e5 100644 --- a/src/components/layout/Grid.js +++ b/src/components/layout/Grid.js @@ -1,13 +1,18 @@ -import { Row, Column } from 'react-basics'; import classNames from 'classnames'; import styles from './Grid.module.css'; +import { mapChildren } from 'react-basics'; + +export function Grid({ className, ...otherProps }) { + return
; +} export function GridRow(props) { - const { className, ...otherProps } = props; - return ; -} - -export function GridColumn(props) { - const { className, ...otherProps } = props; - return ; + const { columns = 'two', className, children, ...otherProps } = props; + return ( +
+ {mapChildren(children, child => { + return
{child}
; + })} +
+ ); } diff --git a/src/components/layout/Grid.module.css b/src/components/layout/Grid.module.css index dc2e8ff6..f72a5f12 100644 --- a/src/components/layout/Grid.module.css +++ b/src/components/layout/Grid.module.css @@ -1,27 +1,52 @@ -.col { - display: flex; - flex-direction: column; - padding: 20px; +.grid { + display: grid; } .row { + display: grid; + grid-template-columns: repeat(6, 1fr); border-top: 1px solid var(--base300); - min-height: 430px; } -.row > .col { +.col { + padding: 20px; + min-height: 430px; border-inline-start: 1px solid var(--base300); } -.row > .col:first-child { +.col:first-child { border-inline-start: 0; padding-inline-start: 0; } -.row > .col:last-child { +.col:last-child { padding-inline-end: 0; } +.col.two { + grid-column: span 3; +} + +.col.three { + grid-column: span 2; +} + +.col.two-one:first-child { + grid-column: span 4; +} + +.col.two-one:last-child { + grid-column: span 2; +} + +.col.one-two:first-child { + grid-column: span 2; +} + +.col.one-two:last-child { + grid-column: span 4; +} + @media only screen and (max-width: 992px) { .row { border: 0; @@ -33,4 +58,11 @@ border-inline-end: 0; padding: 20px 0; } + + .col.two, + .col.three, + .col.one-two, + .col.two-one { + grid-column: span 6 !important; + } } diff --git a/src/components/layout/Page.module.css b/src/components/layout/Page.module.css index 018aecd4..d60413f4 100644 --- a/src/components/layout/Page.module.css +++ b/src/components/layout/Page.module.css @@ -4,7 +4,6 @@ flex-direction: column; background: var(--base50); position: relative; - height: 100%; max-width: 1320px; margin: 0 auto; padding: 0 20px; diff --git a/src/components/metrics/BarChart.js b/src/components/metrics/BarChart.js index 7b94c147..8341fbc7 100644 --- a/src/components/metrics/BarChart.js +++ b/src/components/metrics/BarChart.js @@ -145,7 +145,7 @@ export function BarChart({ }, [datasets, unit, theme, animationDuration, locale]); return ( - <> +
{loading && } @@ -156,7 +156,7 @@ export function BarChart({
{tooltip}
)} - +
); } diff --git a/src/components/metrics/BarChart.module.css b/src/components/metrics/BarChart.module.css index f2e26db1..6af22abe 100644 --- a/src/components/metrics/BarChart.module.css +++ b/src/components/metrics/BarChart.module.css @@ -1,3 +1,7 @@ +.container { + display: grid; +} + .chart { position: relative; height: 400px; diff --git a/src/components/metrics/MetricsBar.js b/src/components/metrics/MetricsBar.js index 2b74e425..84563147 100644 --- a/src/components/metrics/MetricsBar.js +++ b/src/components/metrics/MetricsBar.js @@ -1,8 +1,8 @@ import { useState } from 'react'; import { Loading, cloneChildren } from 'react-basics'; import ErrorMessage from 'components/common/ErrorMessage'; -import styles from './MetricsBar.module.css'; import { formatLongNumber, formatNumber } from 'lib/format'; +import styles from './MetricsBar.module.css'; export function MetricsBar({ children, isLoading, isFetched, error }) { const [format, setFormat] = useState(true); @@ -19,9 +19,12 @@ export function MetricsBar({ children, isLoading, isFetched, error }) {
{isLoading && !isFetched && } {error && } - {cloneChildren(children, child => { - return { format: child.props.format || formatFunc }; - })} + {!isLoading && + !error && + isFetched && + cloneChildren(children, child => { + return { format: child.props.format || formatFunc }; + })}
); } diff --git a/src/components/metrics/MetricsBar.module.css b/src/components/metrics/MetricsBar.module.css index eb33a324..21c9e802 100644 --- a/src/components/metrics/MetricsBar.module.css +++ b/src/components/metrics/MetricsBar.module.css @@ -1,18 +1,11 @@ .bar { - display: flex; - flex-direction: row; - cursor: pointer; - min-height: 110px; + display: grid; + grid-template-columns: repeat(auto-fit, minmax(140px, max-content)); gap: 20px; - flex-wrap: wrap; } -.card { - justify-self: flex-start; -} - -@media only screen and (max-width: 992px) { - .card { - flex-basis: calc(50% - 20px); +@media screen and (max-width: 768px) { + .bar { + grid-template-columns: 1fr 1fr; } } diff --git a/src/styles/index.css b/src/styles/index.css index 4031851a..cdb14c2c 100644 --- a/src/styles/index.css +++ b/src/styles/index.css @@ -52,6 +52,8 @@ main { svg { shape-rendering: geometricPrecision; + width: 10px; + height: 10px; } #__next {