From 5171bdaf472dcd24268bd065adcb70561407d91a Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Wed, 25 Jun 2025 22:53:07 -0700 Subject: [PATCH] Use getRequestDateRange in all routes. --- .../sessions/SessionProperties.module.css | 25 ----------- .../sessions/SessionProperties.tsx | 21 ++++----- .../sessions/SessionsTable.module.css | 5 --- .../[websiteId]/sessions/SessionsTable.tsx | 3 +- .../sessions/SessionsWeekly.module.css | 43 ------------------- .../[websiteId]/sessions/SessionsWeekly.tsx | 16 +++++-- src/app/api/users/[userId]/usage/route.ts | 7 +-- .../[websiteId]/event-data/events/route.ts | 8 ++-- .../[websiteId]/event-data/fields/route.ts | 7 +-- .../event-data/properties/route.ts | 8 ++-- .../[websiteId]/event-data/stats/route.ts | 7 +-- .../[websiteId]/event-data/values/route.ts | 8 ++-- .../api/websites/[websiteId]/events/route.ts | 7 +-- .../[websiteId]/events/series/route.ts | 2 +- .../session-data/properties/route.ts | 8 ++-- .../[websiteId]/session-data/values/route.ts | 8 ++-- .../sessions/[sessionId]/activity/route.ts | 7 +-- .../websites/[websiteId]/sessions/route.ts | 7 +-- .../[websiteId]/sessions/weekly/route.ts | 8 ++-- src/lib/date.ts | 6 ++- src/lib/request.ts | 34 ++++++--------- 21 files changed, 72 insertions(+), 173 deletions(-) delete mode 100644 src/app/(main)/websites/[websiteId]/sessions/SessionProperties.module.css delete mode 100644 src/app/(main)/websites/[websiteId]/sessions/SessionsTable.module.css delete mode 100644 src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.module.css diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionProperties.module.css b/src/app/(main)/websites/[websiteId]/sessions/SessionProperties.module.css deleted file mode 100644 index 479d0994..00000000 --- a/src/app/(main)/websites/[websiteId]/sessions/SessionProperties.module.css +++ /dev/null @@ -1,25 +0,0 @@ -.container { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(420px, 1fr)); - gap: 60px; - margin-bottom: 40px; -} - -.table { - align-self: flex-start; -} - -.link:hover { - cursor: pointer; - color: var(--primary-color); -} - -.title { - text-align: center; - font-weight: bold; - margin: 20px 0; -} - -.chart { - min-height: 620px; -} diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionProperties.tsx b/src/app/(main)/websites/[websiteId]/sessions/SessionProperties.tsx index 7fbb63ea..03e5759a 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/SessionProperties.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionProperties.tsx @@ -1,4 +1,4 @@ -import { DataColumn, DataTable } from '@umami/react-zen'; +import { Grid, DataColumn, DataTable } from '@umami/react-zen'; import { useSessionDataPropertiesQuery, useSessionDataValuesQuery, @@ -8,12 +8,11 @@ import { LoadingPanel } from '@/components/common/LoadingPanel'; import { PieChart } from '@/components/charts/PieChart'; import { useState } from 'react'; import { CHART_COLORS } from '@/lib/constants'; -import styles from './SessionProperties.module.css'; export function SessionProperties({ websiteId }: { websiteId: string }) { const [propertyName, setPropertyName] = useState(''); const { formatMessage, labels } = useMessages(); - const { data, isLoading, isFetched, error } = useSessionDataPropertiesQuery(websiteId); + const { data, isLoading, isFetching, error } = useSessionDataPropertiesQuery(websiteId); const { data: values } = useSessionDataValuesQuery(websiteId, propertyName); const chartData = propertyName && values @@ -30,25 +29,23 @@ export function SessionProperties({ websiteId }: { websiteId: string }) { : null; return ( - -
- + + + {(row: any) => ( -
setPropertyName(row.propertyName)}> - {row.propertyName} -
+
setPropertyName(row.propertyName)}>{row.propertyName}
)}
{propertyName && ( -
-
{propertyName}
+
+
{propertyName}
)} -
+
); } diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.module.css b/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.module.css deleted file mode 100644 index 140ad0bb..00000000 --- a/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.module.css +++ /dev/null @@ -1,5 +0,0 @@ -.link { - display: flex; - align-items: center; - gap: 20px; -} diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx b/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx index 1d7ed0c0..901d5473 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx @@ -2,7 +2,6 @@ import Link from 'next/link'; import { DataColumn, DataTable } from '@umami/react-zen'; import { useFormat, useMessages, useTimezone } from '@/components/hooks'; import { Avatar } from '@/components/common/Avatar'; -import styles from './SessionsTable.module.css'; import { TypeIcon } from '@/components/common/TypeIcon'; export function SessionsTable({ data = [] }: { data: any[]; showDomain?: boolean }) { @@ -14,7 +13,7 @@ export function SessionsTable({ data = [] }: { data: any[]; showDomain?: boolean {(row: any) => ( - + )} diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.module.css b/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.module.css deleted file mode 100644 index 58ab4c2d..00000000 --- a/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.module.css +++ /dev/null @@ -1,43 +0,0 @@ -.week { - display: flex; - justify-content: space-between; - position: relative; -} - -.header { - text-align: center; - font-weight: 700; - margin-bottom: 10px; -} - -.day { - display: flex; - flex-direction: column; - align-items: flex-start; - justify-content: flex-start; - gap: 1px; - position: relative; -} - -.cell { - display: flex; - background-color: var(--base-color-2); - width: 20px; - height: 20px; - margin: auto; - border-radius: 100%; - align-items: flex-start; -} - -.hour { - font-weight: 700; - color: var(--font-color); - height: 20px; -} - -.block { - background-color: var(--primary-color); - width: 20px; - height: 20px; - border-radius: 100%; -} diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.tsx b/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.tsx index 78c5ccdc..d4a45ad3 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.tsx @@ -14,7 +14,7 @@ export function SessionsWeekly({ websiteId }: { websiteId: string }) { .fill(weekStartsOn) .map((d, i) => (d + i) % 7); - const [, max] = data + const [, max = 1] = data ? data.reduce((arr: number[], hours: number[], index: number) => { const min = Math.min(...hours); const max = Math.max(...hours); @@ -69,17 +69,25 @@ export function SessionsWeekly({ websiteId }: { websiteId: string }) { key={index} gap="1" > - + {format(getDayOfWeekAsDate(index), 'EEE', { locale: dateLocale })} {day?.map((count: number, j) => { - const pct = count / max; + const pct = max ? count / max : 0; return ( - + }) { const schema = z.object({ @@ -22,10 +22,7 @@ export async function GET(request: Request, { params }: { params: Promise<{ user } const { userId } = await params; - const { startAt, endAt } = query; - - const startDate = new Date(+startAt); - const endDate = new Date(+endAt); + const { startDate, endDate } = await getRequestDateRange(query); const websites = await getAllUserWebsitesIncludingTeamOwner(userId); diff --git a/src/app/api/websites/[websiteId]/event-data/events/route.ts b/src/app/api/websites/[websiteId]/event-data/events/route.ts index aec7b471..f4b950c4 100644 --- a/src/app/api/websites/[websiteId]/event-data/events/route.ts +++ b/src/app/api/websites/[websiteId]/event-data/events/route.ts @@ -1,5 +1,5 @@ import { z } from 'zod'; -import { parseRequest } from '@/lib/request'; +import { getRequestDateRange, parseRequest } from '@/lib/request'; import { unauthorized, json } from '@/lib/response'; import { canViewWebsite } from '@/lib/auth'; import { getEventDataEvents } from '@/queries/sql/events/getEventDataEvents'; @@ -20,15 +20,13 @@ export async function GET( } const { websiteId } = await params; - const { startAt, endAt, event } = query; + const { event } = query; + const { startDate, endDate } = await getRequestDateRange(query); if (!(await canViewWebsite(auth, websiteId))) { return unauthorized(); } - const startDate = new Date(+startAt); - const endDate = new Date(+endAt); - const data = await getEventDataEvents(websiteId, { startDate, endDate, diff --git a/src/app/api/websites/[websiteId]/event-data/fields/route.ts b/src/app/api/websites/[websiteId]/event-data/fields/route.ts index 60101e45..fa31ca69 100644 --- a/src/app/api/websites/[websiteId]/event-data/fields/route.ts +++ b/src/app/api/websites/[websiteId]/event-data/fields/route.ts @@ -1,5 +1,5 @@ import { z } from 'zod'; -import { parseRequest } from '@/lib/request'; +import { getRequestDateRange, parseRequest } from '@/lib/request'; import { unauthorized, json } from '@/lib/response'; import { canViewWebsite } from '@/lib/auth'; import { getEventDataFields } from '@/queries'; @@ -20,15 +20,12 @@ export async function GET( } const { websiteId } = await params; - const { startAt, endAt } = query; + const { startDate, endDate } = await getRequestDateRange(query); if (!(await canViewWebsite(auth, websiteId))) { return unauthorized(); } - const startDate = new Date(+startAt); - const endDate = new Date(+endAt); - const data = await getEventDataFields(websiteId, { startDate, endDate, diff --git a/src/app/api/websites/[websiteId]/event-data/properties/route.ts b/src/app/api/websites/[websiteId]/event-data/properties/route.ts index fe085f74..2fa7a335 100644 --- a/src/app/api/websites/[websiteId]/event-data/properties/route.ts +++ b/src/app/api/websites/[websiteId]/event-data/properties/route.ts @@ -1,5 +1,5 @@ import { z } from 'zod'; -import { parseRequest } from '@/lib/request'; +import { getRequestDateRange, parseRequest } from '@/lib/request'; import { unauthorized, json } from '@/lib/response'; import { canViewWebsite } from '@/lib/auth'; import { getEventDataProperties } from '@/queries'; @@ -21,15 +21,13 @@ export async function GET( } const { websiteId } = await params; - const { startAt, endAt, propertyName } = query; + const { propertyName } = query; + const { startDate, endDate } = await getRequestDateRange(query); if (!(await canViewWebsite(auth, websiteId))) { return unauthorized(); } - const startDate = new Date(+startAt); - const endDate = new Date(+endAt); - const data = await getEventDataProperties(websiteId, { startDate, endDate, propertyName }); return json(data); diff --git a/src/app/api/websites/[websiteId]/event-data/stats/route.ts b/src/app/api/websites/[websiteId]/event-data/stats/route.ts index 6928aa1e..ccc45b75 100644 --- a/src/app/api/websites/[websiteId]/event-data/stats/route.ts +++ b/src/app/api/websites/[websiteId]/event-data/stats/route.ts @@ -1,5 +1,5 @@ import { z } from 'zod'; -import { parseRequest } from '@/lib/request'; +import { getRequestDateRange, parseRequest } from '@/lib/request'; import { unauthorized, json } from '@/lib/response'; import { canViewWebsite } from '@/lib/auth'; import { getEventDataStats } from '@/queries'; @@ -21,15 +21,12 @@ export async function GET( } const { websiteId } = await params; - const { startAt, endAt } = query; + const { startDate, endDate } = await getRequestDateRange(query); if (!(await canViewWebsite(auth, websiteId))) { return unauthorized(); } - const startDate = new Date(+startAt); - const endDate = new Date(+endAt); - const data = await getEventDataStats(websiteId, { startDate, endDate }); return json(data); diff --git a/src/app/api/websites/[websiteId]/event-data/values/route.ts b/src/app/api/websites/[websiteId]/event-data/values/route.ts index 2a912439..c25dc014 100644 --- a/src/app/api/websites/[websiteId]/event-data/values/route.ts +++ b/src/app/api/websites/[websiteId]/event-data/values/route.ts @@ -1,5 +1,5 @@ import { z } from 'zod'; -import { parseRequest } from '@/lib/request'; +import { getRequestDateRange, parseRequest } from '@/lib/request'; import { unauthorized, json } from '@/lib/response'; import { canViewWebsite } from '@/lib/auth'; import { getEventDataValues } from '@/queries'; @@ -22,15 +22,13 @@ export async function GET( } const { websiteId } = await params; - const { startAt, endAt, eventName, propertyName } = query; + const { eventName, propertyName } = query; + const { startDate, endDate } = await getRequestDateRange(query); if (!(await canViewWebsite(auth, websiteId))) { return unauthorized(); } - const startDate = new Date(+startAt); - const endDate = new Date(+endAt); - const data = await getEventDataValues(websiteId, { startDate, endDate, diff --git a/src/app/api/websites/[websiteId]/events/route.ts b/src/app/api/websites/[websiteId]/events/route.ts index 66eaba2c..fb3db328 100644 --- a/src/app/api/websites/[websiteId]/events/route.ts +++ b/src/app/api/websites/[websiteId]/events/route.ts @@ -1,5 +1,5 @@ import { z } from 'zod'; -import { parseRequest } from '@/lib/request'; +import { getRequestDateRange, parseRequest } from '@/lib/request'; import { unauthorized, json } from '@/lib/response'; import { canViewWebsite } from '@/lib/auth'; import { pagingParams } from '@/lib/schema'; @@ -22,15 +22,12 @@ export async function GET( } const { websiteId } = await params; - const { startAt, endAt } = query; + const { startDate, endDate } = await getRequestDateRange(query); if (!(await canViewWebsite(auth, websiteId))) { return unauthorized(); } - const startDate = new Date(+startAt); - const endDate = new Date(+endAt); - const data = await getWebsiteEvents(websiteId, { startDate, endDate }, query); return json(data); diff --git a/src/app/api/websites/[websiteId]/events/series/route.ts b/src/app/api/websites/[websiteId]/events/series/route.ts index da4b0d4f..43bd23a7 100644 --- a/src/app/api/websites/[websiteId]/events/series/route.ts +++ b/src/app/api/websites/[websiteId]/events/series/route.ts @@ -12,7 +12,7 @@ export async function GET( const schema = z.object({ startAt: z.coerce.number().int(), endAt: z.coerce.number().int(), - unit: unitParam, + unit: unitParam.optional(), timezone: timezoneParam, ...filterParams, }); diff --git a/src/app/api/websites/[websiteId]/session-data/properties/route.ts b/src/app/api/websites/[websiteId]/session-data/properties/route.ts index a6d9e2a4..901dcdf5 100644 --- a/src/app/api/websites/[websiteId]/session-data/properties/route.ts +++ b/src/app/api/websites/[websiteId]/session-data/properties/route.ts @@ -1,5 +1,5 @@ import { z } from 'zod'; -import { parseRequest } from '@/lib/request'; +import { getRequestDateRange, parseRequest } from '@/lib/request'; import { unauthorized, json } from '@/lib/response'; import { canViewWebsite } from '@/lib/auth'; import { getSessionDataProperties } from '@/queries'; @@ -20,16 +20,14 @@ export async function GET( return error(); } - const { startAt, endAt, propertyName } = query; const { websiteId } = await params; + const { propertyName } = query; + const { startDate, endDate } = await getRequestDateRange(query); if (!(await canViewWebsite(auth, websiteId))) { return unauthorized(); } - const startDate = new Date(+startAt); - const endDate = new Date(+endAt); - const data = await getSessionDataProperties(websiteId, { startDate, endDate, propertyName }); return json(data); diff --git a/src/app/api/websites/[websiteId]/session-data/values/route.ts b/src/app/api/websites/[websiteId]/session-data/values/route.ts index d950da34..0730a70b 100644 --- a/src/app/api/websites/[websiteId]/session-data/values/route.ts +++ b/src/app/api/websites/[websiteId]/session-data/values/route.ts @@ -1,5 +1,5 @@ import { canViewWebsite } from '@/lib/auth'; -import { parseRequest } from '@/lib/request'; +import { getRequestDateRange, parseRequest } from '@/lib/request'; import { json, unauthorized } from '@/lib/response'; import { getSessionDataValues } from '@/queries'; import { z } from 'zod'; @@ -20,16 +20,14 @@ export async function GET( return error(); } - const { startAt, endAt, propertyName } = query; + const { propertyName } = query; const { websiteId } = await params; + const { startDate, endDate } = await getRequestDateRange(query); if (!(await canViewWebsite(auth, websiteId))) { return unauthorized(); } - const startDate = new Date(+startAt); - const endDate = new Date(+endAt); - const data = await getSessionDataValues(websiteId, { startDate, endDate, diff --git a/src/app/api/websites/[websiteId]/sessions/[sessionId]/activity/route.ts b/src/app/api/websites/[websiteId]/sessions/[sessionId]/activity/route.ts index aac40c38..831d0064 100644 --- a/src/app/api/websites/[websiteId]/sessions/[sessionId]/activity/route.ts +++ b/src/app/api/websites/[websiteId]/sessions/[sessionId]/activity/route.ts @@ -1,5 +1,5 @@ import { z } from 'zod'; -import { parseRequest } from '@/lib/request'; +import { parseRequest, getRequestDateRange } from '@/lib/request'; import { unauthorized, json } from '@/lib/response'; import { canViewWebsite } from '@/lib/auth'; import { getSessionActivity } from '@/queries'; @@ -20,15 +20,12 @@ export async function GET( } const { websiteId, sessionId } = await params; - const { startAt, endAt } = query; + const { startDate, endDate } = await getRequestDateRange(query); if (!(await canViewWebsite(auth, websiteId))) { return unauthorized(); } - const startDate = new Date(+startAt); - const endDate = new Date(+endAt); - const data = await getSessionActivity(websiteId, sessionId, startDate, endDate); return json(data); diff --git a/src/app/api/websites/[websiteId]/sessions/route.ts b/src/app/api/websites/[websiteId]/sessions/route.ts index 5a14f00f..6f8a671e 100644 --- a/src/app/api/websites/[websiteId]/sessions/route.ts +++ b/src/app/api/websites/[websiteId]/sessions/route.ts @@ -1,5 +1,5 @@ import { z } from 'zod'; -import { parseRequest } from '@/lib/request'; +import { getRequestDateRange, parseRequest } from '@/lib/request'; import { unauthorized, json } from '@/lib/response'; import { canViewWebsite } from '@/lib/auth'; import { pagingParams } from '@/lib/schema'; @@ -21,16 +21,13 @@ export async function GET( return error(); } + const { startDate, endDate } = await getRequestDateRange(query); const { websiteId } = await params; - const { startAt, endAt } = query; if (!(await canViewWebsite(auth, websiteId))) { return unauthorized(); } - const startDate = new Date(+startAt); - const endDate = new Date(+endAt); - const data = await getWebsiteSessions(websiteId, { startDate, endDate }, query); return json(data); diff --git a/src/app/api/websites/[websiteId]/sessions/weekly/route.ts b/src/app/api/websites/[websiteId]/sessions/weekly/route.ts index 20be378d..a8216123 100644 --- a/src/app/api/websites/[websiteId]/sessions/weekly/route.ts +++ b/src/app/api/websites/[websiteId]/sessions/weekly/route.ts @@ -1,5 +1,5 @@ import { z } from 'zod'; -import { parseRequest } from '@/lib/request'; +import { getRequestDateRange, parseRequest } from '@/lib/request'; import { unauthorized, json } from '@/lib/response'; import { canViewWebsite } from '@/lib/auth'; import { pagingParams, timezoneParam } from '@/lib/schema'; @@ -23,15 +23,13 @@ export async function GET( } const { websiteId } = await params; - const { startAt, endAt, timezone } = query; + const { timezone } = query; + const { startDate, endDate } = await getRequestDateRange(query); if (!(await canViewWebsite(auth, websiteId))) { return unauthorized(); } - const startDate = new Date(+startAt); - const endDate = new Date(+endAt); - const data = await getWebsiteSessionsWeekly(websiteId, { startDate, endDate, timezone }); return json(data); diff --git a/src/lib/date.ts b/src/lib/date.ts index 79d9952e..f740791a 100644 --- a/src/lib/date.ts +++ b/src/lib/date.ts @@ -310,7 +310,11 @@ export function getDayOfWeekAsDate(dayOfWeek: number) { return currentDate; } -export function formatDate(date: string | number | Date, dateFormat: string, locale = 'en-US') { +export function formatDate( + date: string | number | Date, + dateFormat: string = 'PPpp', + locale = 'en-US', +) { return format(typeof date === 'string' ? new Date(date) : date, dateFormat, { locale: getDateLocale(locale), }); diff --git a/src/lib/request.ts b/src/lib/request.ts index 2481ae9f..0f07fa87 100644 --- a/src/lib/request.ts +++ b/src/lib/request.ts @@ -1,9 +1,8 @@ import { z } from 'zod/v4'; import { FILTER_COLUMNS } from '@/lib/constants'; import { badRequest, unauthorized } from '@/lib/response'; -import { getAllowedUnits, getMinimumUnit } from '@/lib/date'; +import { getAllowedUnits, getCompareDate, getMinimumUnit } from '@/lib/date'; import { checkAuth } from '@/lib/auth'; -import { getWebsiteDateRange } from '@/queries'; export async function parseRequest( request: Request, @@ -48,31 +47,26 @@ export async function getJsonBody(request: Request) { } } -export async function getRequestDateRange(query: Record) { - const { websiteId, startAt, endAt, unit } = query; - - // All-time - if (+startAt === 0 && +endAt === 1) { - const result = await getWebsiteDateRange(websiteId as string); - const { min, max } = result[0]; - const startDate = new Date(min); - const endDate = new Date(max); - - return { - startDate, - endDate, - unit: getMinimumUnit(startDate, endDate), - }; - } +export async function getRequestDateRange(query: Record) { + const { startAt, endAt, unit, compare } = query; const startDate = new Date(+startAt); const endDate = new Date(+endAt); - const minUnit = getMinimumUnit(startDate, endDate); + + const { startDate: compareStartDate, endDate: compareEndDate } = getCompareDate( + compare, + startDate, + endDate, + ); return { startDate, endDate, - unit: (getAllowedUnits(startDate, endDate).includes(unit as string) ? unit : minUnit) as string, + compareStartDate, + compareEndDate, + unit: getAllowedUnits(startDate, endDate).includes(unit) + ? unit + : getMinimumUnit(startDate, endDate), }; }