diff --git a/src/app/(main)/websites/[websiteId]/WebsiteChart.module.css b/src/app/(main)/websites/[websiteId]/WebsiteChart.module.css deleted file mode 100644 index b795047a9..000000000 --- a/src/app/(main)/websites/[websiteId]/WebsiteChart.module.css +++ /dev/null @@ -1,17 +0,0 @@ -.container { - position: relative; - display: flex; - flex-direction: column; - align-self: stretch; -} - -.chart { - position: relative; - overflow: hidden; -} - -.title { - font-size: var(--font-size-lg); - line-height: 60px; - font-weight: 600; -} diff --git a/src/app/(main)/websites/[websiteId]/WebsiteCompareTables.module.css b/src/app/(main)/websites/[websiteId]/WebsiteCompareTables.module.css deleted file mode 100644 index c4821e886..000000000 --- a/src/app/(main)/websites/[websiteId]/WebsiteCompareTables.module.css +++ /dev/null @@ -1,14 +0,0 @@ -.container { - margin-bottom: 60px; -} - -.nav { - width: 200px; - margin-top: 40px; -} - -.title { - color: var(--base800); - text-align: center; - font-weight: 700; -} diff --git a/src/app/(main)/websites/[websiteId]/WebsiteControls.tsx b/src/app/(main)/websites/[websiteId]/WebsiteControls.tsx index fb84df870..1e36b729b 100644 --- a/src/app/(main)/websites/[websiteId]/WebsiteControls.tsx +++ b/src/app/(main)/websites/[websiteId]/WebsiteControls.tsx @@ -2,23 +2,27 @@ import { Column, Row } from '@umami/react-zen'; import { WebsiteFilterButton } from '@/app/(main)/websites/[websiteId]/WebsiteFilterButton'; import { WebsiteDateFilter } from '@/components/input/WebsiteDateFilter'; import { FilterBar } from '@/components/input/FilterBar'; +import { WebsiteMonthSelect } from '@/components/input/WebsiteMonthSelect'; export function WebsiteControls({ websiteId, allowFilter = true, + allowDateFilter = true, + allowMonthFilter, allowCompare, }: { websiteId: string; allowFilter?: boolean; allowCompare?: boolean; + allowDateFilter?: boolean; + allowMonthFilter?: boolean; }) { return ( {allowFilter && } - - - + {allowDateFilter && } + {allowMonthFilter && } diff --git a/src/app/(main)/websites/[websiteId]/WebsiteHeader.tsx b/src/app/(main)/websites/[websiteId]/WebsiteHeader.tsx index d0a17b7f7..4c247dcd2 100644 --- a/src/app/(main)/websites/[websiteId]/WebsiteHeader.tsx +++ b/src/app/(main)/websites/[websiteId]/WebsiteHeader.tsx @@ -14,15 +14,17 @@ export function WebsiteHeader() { return ( } showBorder={false}> - + - - - - - - Edit - + + + + + + + Edit + + ); diff --git a/src/app/(main)/websites/[websiteId]/WebsiteMetricsBar.module.css b/src/app/(main)/websites/[websiteId]/WebsiteMetricsBar.module.css deleted file mode 100644 index 6c5a0e569..000000000 --- a/src/app/(main)/websites/[websiteId]/WebsiteMetricsBar.module.css +++ /dev/null @@ -1,52 +0,0 @@ -.container { - display: grid; - grid-template-columns: 2fr 1fr; - justify-content: space-between; - align-items: center; - background: var(--base50); - z-index: var(--z-index-above); - min-height: 120px; - padding-bottom: 20px; -} - -.actions { - display: flex; - flex-direction: column; - align-items: flex-end; - gap: 10px; - flex-wrap: wrap; -} - -.vs { - display: flex; - align-items: center; - justify-content: flex-end; - flex-basis: 100%; - gap: 10px; -} - -.dropdown { - min-width: 200px; -} - -@media screen and (max-width: 1200px) { - .container { - grid-template-columns: 1fr; - } - - .actions { - margin: 20px 0; - } -} - -@media screen and (min-width: 992px) { - .sticky { - position: sticky; - top: -1px; - } - - .isSticky { - padding: 10px 0; - border-bottom: 1px solid var(--base300); - } -} diff --git a/src/app/(main)/websites/[websiteId]/events/EventsMetricsBar.tsx b/src/app/(main)/websites/[websiteId]/events/EventsMetricsBar.tsx index 5200ab168..2712c985e 100644 --- a/src/app/(main)/websites/[websiteId]/events/EventsMetricsBar.tsx +++ b/src/app/(main)/websites/[websiteId]/events/EventsMetricsBar.tsx @@ -11,28 +11,30 @@ export function EventsMetricsBar({ websiteId }: { websiteId: string }) { return ( - - - - - - + {data && ( + + + + + + + )} ); } diff --git a/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx b/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx index c46ef0977..8e9655281 100644 --- a/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx +++ b/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx @@ -67,7 +67,7 @@ export function RealtimeLog({ data }: { data: RealtimeData }) { country: string; device: string; }) => { - const { __type, eventName, urlPath: url, browser, os, country, device } = log; + const { __type, eventName, urlPath, browser, os, country, device } = log; if (__type === TYPE_EVENT) { return formatMessage(messages.eventLog, { @@ -75,12 +75,12 @@ export function RealtimeLog({ data }: { data: RealtimeData }) { url: ( - {url} + {urlPath} ), }); @@ -89,12 +89,12 @@ export function RealtimeLog({ data }: { data: RealtimeData }) { if (__type === TYPE_PAGEVIEW) { return ( - {url} + {urlPath} ); } diff --git a/src/app/(main)/websites/[websiteId]/reports/ReportsNav.tsx b/src/app/(main)/websites/[websiteId]/reports/ReportsNav.tsx index 6313ca46a..f073a12d9 100644 --- a/src/app/(main)/websites/[websiteId]/reports/ReportsNav.tsx +++ b/src/app/(main)/websites/[websiteId]/reports/ReportsNav.tsx @@ -66,7 +66,13 @@ export function ReportsNav({ websiteId }: { websiteId: string }) { const isSelected = selected === id; return ( - + {icon} diff --git a/src/app/(main)/websites/[websiteId]/reports/retention/Retention.tsx b/src/app/(main)/websites/[websiteId]/reports/retention/Retention.tsx index 1667c4831..b4bd01a78 100644 --- a/src/app/(main)/websites/[websiteId]/reports/retention/Retention.tsx +++ b/src/app/(main)/websites/[websiteId]/reports/retention/Retention.tsx @@ -1,7 +1,7 @@ import { ReactNode } from 'react'; import { Grid, Row, Column, Text, Icon } from '@umami/react-zen'; import { Users } from '@/components/icons'; -import { useMessages, useLocale, useResultQuery } from '@/components/hooks'; +import { useMessages, useLocale, useResultQuery, useTimezone } from '@/components/hooks'; import { formatDate } from '@/lib/date'; import { formatLongNumber } from '@/lib/format'; import { Panel } from '@/components/common/Panel'; @@ -19,8 +19,10 @@ export interface RetentionProps { export function Retention({ websiteId, days = DAYS, startDate, endDate }: RetentionProps) { const { formatMessage, labels } = useMessages(); const { locale } = useLocale(); + const { timezone } = useTimezone(); const { data, error, isLoading } = useResultQuery('retention', { websiteId, + timezone, dateRange: { startDate, endDate, @@ -51,54 +53,56 @@ export function Retention({ websiteId, days = DAYS, startDate, endDate }: Retent return ( - - - - - - {formatMessage(labels.cohort)} - - - {days.map(n => ( - - - {formatMessage(labels.day)} {n} + {data && ( + + + + + + {formatMessage(labels.cohort)} - ))} - - {rows.map(({ date, visitors, records }: any, rowIndex: number) => { - return ( - - - {formatDate(date, 'PP', locale)} - - - - - {formatLongNumber(visitors)} - + {days.map(n => ( + + + {formatMessage(labels.day)} {n} + - {days.map(day => { - if (totalDays - rowIndex < day) { - return null; - } - const percentage = records.filter(a => a.day === day)[0]?.percentage; - return ( - {percentage ? `${Number(percentage).toFixed(2)}%` : ''} - ); - })} - - ); - })} - - + ))} + + {rows.map(({ date, visitors, records }: any, rowIndex: number) => { + return ( + + + {formatDate(date, 'PP', locale)} + + + + + {formatLongNumber(visitors)} + + + {days.map(day => { + if (totalDays - rowIndex < day) { + return null; + } + const percentage = records.filter(a => a.day === day)[0]?.percentage; + return ( + {percentage ? `${Number(percentage).toFixed(2)}%` : ''} + ); + })} + + ); + })} + + + )} ); } diff --git a/src/app/(main)/websites/[websiteId]/reports/retention/RetentionPage.tsx b/src/app/(main)/websites/[websiteId]/reports/retention/RetentionPage.tsx index 71ffc6fb6..64cf68232 100644 --- a/src/app/(main)/websites/[websiteId]/reports/retention/RetentionPage.tsx +++ b/src/app/(main)/websites/[websiteId]/reports/retention/RetentionPage.tsx @@ -3,16 +3,20 @@ import { Column } from '@umami/react-zen'; import { Retention } from './Retention'; import { WebsiteControls } from '@/app/(main)/websites/[websiteId]/WebsiteControls'; import { useDateRange } from '@/components/hooks'; +import { endOfMonth, startOfMonth } from 'date-fns'; export function RetentionPage({ websiteId }: { websiteId: string }) { const { - dateRange: { startDate, endDate }, + dateRange: { startDate }, } = useDateRange(websiteId); + const monthStartDate = startOfMonth(startDate); + const monthEndDate = endOfMonth(startDate); + return ( - - + + ); } diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionsMetricsBar.tsx b/src/app/(main)/websites/[websiteId]/sessions/SessionsMetricsBar.tsx index 1da45000e..b959f0dac 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/SessionsMetricsBar.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsMetricsBar.tsx @@ -11,28 +11,30 @@ export function SessionsMetricsBar({ websiteId }: { websiteId: string }) { return ( - - - - - - + {data && ( + + + + + + + )} ); } diff --git a/src/app/api/reports/retention/route.ts b/src/app/api/reports/retention/route.ts index 04842121b..cc7433aa7 100644 --- a/src/app/api/reports/retention/route.ts +++ b/src/app/api/reports/retention/route.ts @@ -14,6 +14,7 @@ export async function POST(request: Request) { const { websiteId, dateRange: { startDate, endDate }, + timezone, } = body; if (!(await canViewWebsite(auth, websiteId))) { @@ -23,6 +24,7 @@ export async function POST(request: Request) { const data = await getRetention(websiteId, { startDate: new Date(startDate), endDate: new Date(endDate), + timezone, }); return json(data); diff --git a/src/components/common/Panel.tsx b/src/components/common/Panel.tsx index 22f9ab9be..7341b3b5b 100644 --- a/src/components/common/Panel.tsx +++ b/src/components/common/Panel.tsx @@ -62,7 +62,7 @@ export function Panel({ - {formatMessage(labels.expand)} + {formatMessage(labels.maximize)} )} diff --git a/src/components/hooks/useDateRange.ts b/src/components/hooks/useDateRange.ts index 6a6e16d42..795c42398 100644 --- a/src/components/hooks/useDateRange.ts +++ b/src/components/hooks/useDateRange.ts @@ -22,7 +22,7 @@ export function useDateRange(websiteId?: string) { ); const dateRange = useMemo( () => (offset ? getOffsetDateRange(dateRangeObject, +offset) : dateRangeObject), - [date, offset], + [date, offset, websiteConfig], ); const dateCompare = useWebsites(state => state[websiteId]?.dateCompare || DEFAULT_DATE_COMPARE); diff --git a/src/components/hooks/useFilterParams.ts b/src/components/hooks/useFilterParams.ts index 55deed141..bee2a6494 100644 --- a/src/components/hooks/useFilterParams.ts +++ b/src/components/hooks/useFilterParams.ts @@ -8,7 +8,7 @@ export function useFilterParams(websiteId: string) { const { timezone, toUtc } = useTimezone(); const { query: { - url, + path, referrer, title, query, @@ -29,7 +29,7 @@ export function useFilterParams(websiteId: string) { endAt: +toUtc(endDate), unit, timezone, - url, + path, referrer, title, query, diff --git a/src/components/input/DateFilter.tsx b/src/components/input/DateFilter.tsx index 72be6c6de..3e2c00ed4 100644 --- a/src/components/input/DateFilter.tsx +++ b/src/components/input/DateFilter.tsx @@ -99,7 +99,7 @@ export function DateFilter({ }; return ( - + + {months.map(m => { + return ( + + {formatDate(new Date(year, m, 1), 'MMMM', locale)} + + ); + })} + + ); } diff --git a/src/components/input/WebsiteDateFilter.tsx b/src/components/input/WebsiteDateFilter.tsx index afb586319..e78bcd8dc 100644 --- a/src/components/input/WebsiteDateFilter.tsx +++ b/src/components/input/WebsiteDateFilter.tsx @@ -55,7 +55,7 @@ export function WebsiteDateFilter({ }; return ( - + {showButtons && !isAllTime && !isCustomRange && (