From 6eefb4173cf2e5a8e789cd60be326966f8c708dd Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Mon, 27 Oct 2025 13:54:15 -0700 Subject: [PATCH 1/8] add improved truncation between tablets and phones --- src/components/hooks/useMobile.ts | 3 ++- src/components/metrics/ListTable.tsx | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/hooks/useMobile.ts b/src/components/hooks/useMobile.ts index e3afc5ee6..6b40f3da1 100644 --- a/src/components/hooks/useMobile.ts +++ b/src/components/hooks/useMobile.ts @@ -3,6 +3,7 @@ import { useBreakpoint } from '@umami/react-zen'; export function useMobile() { const breakpoint = useBreakpoint(); const isMobile = ['xs', 'sm', 'md'].includes(breakpoint); + const isPhone = ['xs', 'sm'].includes(breakpoint); - return { breakpoint, isMobile }; + return { breakpoint, isMobile, isPhone }; } diff --git a/src/components/metrics/ListTable.tsx b/src/components/metrics/ListTable.tsx index 3aef2b287..303556b01 100644 --- a/src/components/metrics/ListTable.tsx +++ b/src/components/metrics/ListTable.tsx @@ -42,7 +42,7 @@ export function ListTable({ currency, }: ListTableProps) { const { formatMessage, labels } = useMessages(); - const { isMobile } = useMobile(); + const { isPhone } = useMobile(); const getRow = (row: ListData, index: number) => { const { label, count, percent } = row; @@ -57,7 +57,7 @@ export function ListTable({ showPercentage={showPercentage} change={renderChange ? renderChange(row, index) : null} currency={currency} - isMobile={isMobile} + isMobile={isPhone} /> ); }; From c81b1c16c8de2cc1752e6d138b709c5e499d4d03 Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Wed, 29 Oct 2025 10:10:46 -0700 Subject: [PATCH 2/8] fix geteventdatavalues query --- src/queries/sql/events/getEventDataValues.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/queries/sql/events/getEventDataValues.ts b/src/queries/sql/events/getEventDataValues.ts index ad7dd1a37..e1975e2e4 100644 --- a/src/queries/sql/events/getEventDataValues.ts +++ b/src/queries/sql/events/getEventDataValues.ts @@ -47,7 +47,6 @@ async function relationalQuery( where event_data.website_id = {{websiteId::uuid}} and event_data.created_at between {{startDate}} and {{endDate}} and event_data.data_key = {{propertyName}} - and website_event.event_name = {{eventName}} ${filterQuery} group by value order by 2 desc From ef55b63a3b20bbb98fd56df3af73c0b188d4a169 Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Wed, 29 Oct 2025 11:04:54 -0700 Subject: [PATCH 3/8] Fix admin layout and data refresh after update/delete --- src/app/(main)/admin/AdminLayout.tsx | 15 +++++++++--- .../(main)/admin/teams/AdminTeamsTable.tsx | 24 +++++++++++-------- .../admin/users/[userId]/UserEditForm.tsx | 1 + .../(main)/teams/[teamId]/TeamDeleteForm.tsx | 1 + .../[websiteId]/settings/WebsiteEditForm.tsx | 1 + 5 files changed, 29 insertions(+), 13 deletions(-) diff --git a/src/app/(main)/admin/AdminLayout.tsx b/src/app/(main)/admin/AdminLayout.tsx index 8b1387854..4c902d0fe 100644 --- a/src/app/(main)/admin/AdminLayout.tsx +++ b/src/app/(main)/admin/AdminLayout.tsx @@ -46,8 +46,15 @@ export function AdminLayout({ children }: { children: ReactNode }) { ?.find(({ path }) => path && pathname.startsWith(path))?.id; return ( - - + + - {children} + + {children} + ); } diff --git a/src/app/(main)/admin/teams/AdminTeamsTable.tsx b/src/app/(main)/admin/teams/AdminTeamsTable.tsx index d7c99a267..e54532583 100644 --- a/src/app/(main)/admin/teams/AdminTeamsTable.tsx +++ b/src/app/(main)/admin/teams/AdminTeamsTable.tsx @@ -1,11 +1,11 @@ -import { useState } from 'react'; -import { Row, Text, Icon, DataTable, DataColumn, MenuItem, Modal } from '@umami/react-zen'; -import Link from 'next/link'; -import { Trash } from '@/components/icons'; -import { useMessages } from '@/components/hooks'; -import { Edit } from '@/components/icons'; -import { MenuButton } from '@/components/input/MenuButton'; import { DateDistance } from '@/components/common/DateDistance'; +import { useMessages } from '@/components/hooks'; +import { Edit, Trash } from '@/components/icons'; +import { MenuButton } from '@/components/input/MenuButton'; +import { DataColumn, DataTable, Dialog, Icon, MenuItem, Modal, Row, Text } from '@umami/react-zen'; +import { TeamDeleteForm } from '../../teams/[teamId]/TeamDeleteForm'; +import Link from 'next/link'; +import { useState } from 'react'; export function AdminTeamsTable({ data = [], @@ -15,7 +15,7 @@ export function AdminTeamsTable({ showActions?: boolean; }) { const { formatMessage, labels } = useMessages(); - const [deleteUser, setDeleteUser] = useState(null); + const [deleteTeam, setDeleteTeam] = useState(null); return ( <> @@ -60,7 +60,7 @@ export function AdminTeamsTable({ setDeleteUser(row)} + onAction={() => setDeleteTeam(id)} data-test="link-button-delete" > @@ -76,7 +76,11 @@ export function AdminTeamsTable({ )} - + + + setDeleteTeam(null)} /> + + ); } diff --git a/src/app/(main)/admin/users/[userId]/UserEditForm.tsx b/src/app/(main)/admin/users/[userId]/UserEditForm.tsx index ac7875db0..142adaa66 100644 --- a/src/app/(main)/admin/users/[userId]/UserEditForm.tsx +++ b/src/app/(main)/admin/users/[userId]/UserEditForm.tsx @@ -22,6 +22,7 @@ export function UserEditForm({ userId, onSave }: { userId: string; onSave?: () = await mutateAsync(data, { onSuccess: async () => { toast(formatMessage(messages.saved)); + touch('users'); touch(`user:${user.id}`); onSave?.(); }, diff --git a/src/app/(main)/teams/[teamId]/TeamDeleteForm.tsx b/src/app/(main)/teams/[teamId]/TeamDeleteForm.tsx index cf1ce6188..7adc9b341 100644 --- a/src/app/(main)/teams/[teamId]/TeamDeleteForm.tsx +++ b/src/app/(main)/teams/[teamId]/TeamDeleteForm.tsx @@ -19,6 +19,7 @@ export function TeamDeleteForm({ await mutateAsync(null, { onSuccess: async () => { touch('teams'); + touch(`teams:${teamId}`); onSave?.(); onClose?.(); }, diff --git a/src/app/(main)/websites/[websiteId]/settings/WebsiteEditForm.tsx b/src/app/(main)/websites/[websiteId]/settings/WebsiteEditForm.tsx index 50598199a..c7cb3d89f 100644 --- a/src/app/(main)/websites/[websiteId]/settings/WebsiteEditForm.tsx +++ b/src/app/(main)/websites/[websiteId]/settings/WebsiteEditForm.tsx @@ -11,6 +11,7 @@ export function WebsiteEditForm({ websiteId, onSave }: { websiteId: string; onSa await mutateAsync(data, { onSuccess: async () => { toast(formatMessage(messages.saved)); + touch('websites'); touch(`website:${website.id}`); onSave?.(); }, From 72fba187dbe520dfdf68c4f4029989c9ee929af5 Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Wed, 29 Oct 2025 11:32:52 -0700 Subject: [PATCH 4/8] separate Admin/Settings Nav and add to MobileNav --- src/app/(main)/MobileNav.tsx | 20 +++++--- src/app/(main)/admin/AdminLayout.tsx | 48 ++------------------ src/app/(main)/admin/AdminNav.tsx | 48 ++++++++++++++++++++ src/app/(main)/settings/SettingsLayout.tsx | 49 +------------------- src/app/(main)/settings/SettingsNav.tsx | 53 ++++++++++++++++++++++ 5 files changed, 121 insertions(+), 97 deletions(-) create mode 100644 src/app/(main)/admin/AdminNav.tsx create mode 100644 src/app/(main)/settings/SettingsNav.tsx diff --git a/src/app/(main)/MobileNav.tsx b/src/app/(main)/MobileNav.tsx index d3ff51ddc..a19449606 100644 --- a/src/app/(main)/MobileNav.tsx +++ b/src/app/(main)/MobileNav.tsx @@ -1,15 +1,19 @@ -import { Row, NavMenu, NavMenuItem, IconLabel, Text, Grid } from '@umami/react-zen'; -import { Globe, Grid2x2, LinkIcon } from '@/components/icons'; -import { useMessages, useNavigation } from '@/components/hooks'; -import Link from 'next/link'; import { WebsiteNav } from '@/app/(main)/websites/[websiteId]/WebsiteNav'; -import { Logo } from '@/components/svg'; -import { NavButton } from '@/components/input/NavButton'; +import { useMessages, useNavigation } from '@/components/hooks'; +import { Globe, Grid2x2, LinkIcon } from '@/components/icons'; import { MobileMenuButton } from '@/components/input/MobileMenuButton'; +import { NavButton } from '@/components/input/NavButton'; +import { Logo } from '@/components/svg'; +import { Grid, IconLabel, NavMenu, NavMenuItem, Row, Text } from '@umami/react-zen'; +import Link from 'next/link'; +import { AdminNav } from './admin/AdminNav'; +import { SettingsNav } from './settings/SettingsNav'; export function MobileNav() { const { formatMessage, labels } = useMessages(); - const { websiteId, renderUrl } = useNavigation(); + const { pathname, websiteId, renderUrl } = useNavigation(); + const isAdmin = pathname.includes('/admin'); + const isSettings = pathname.includes('/settings'); const links = [ { @@ -51,6 +55,8 @@ export function MobileNav() { })} {websiteId && } + {isAdmin && } + {isSettings && } ); }} diff --git a/src/app/(main)/admin/AdminLayout.tsx b/src/app/(main)/admin/AdminLayout.tsx index 4c902d0fe..3cc5aed39 100644 --- a/src/app/(main)/admin/AdminLayout.tsx +++ b/src/app/(main)/admin/AdminLayout.tsx @@ -1,50 +1,17 @@ 'use client'; -import { ReactNode } from 'react'; -import { Grid, Column } from '@umami/react-zen'; -import { useLoginQuery, useMessages, useNavigation } from '@/components/hooks'; -import { User, Users, Globe } from '@/components/icons'; -import { SideMenu } from '@/components/common/SideMenu'; import { PageBody } from '@/components/common/PageBody'; +import { useLoginQuery } from '@/components/hooks'; +import { Column, Grid } from '@umami/react-zen'; +import { ReactNode } from 'react'; +import { AdminNav } from './AdminNav'; export function AdminLayout({ children }: { children: ReactNode }) { const { user } = useLoginQuery(); - const { formatMessage, labels } = useMessages(); - const { pathname } = useNavigation(); if (!user.isAdmin || process.env.cloudMode) { return null; } - const items = [ - { - label: formatMessage(labels.manage), - items: [ - { - id: 'users', - label: formatMessage(labels.users), - path: '/admin/users', - icon: , - }, - { - id: 'websites', - label: formatMessage(labels.websites), - path: '/admin/websites', - icon: , - }, - { - id: 'teams', - label: formatMessage(labels.teams), - path: '/admin/teams', - icon: , - }, - ], - }, - ]; - - const selectedKey = items - .flatMap(e => e.items) - ?.find(({ path }) => path && pathname.startsWith(path))?.id; - return ( - + {children} diff --git a/src/app/(main)/admin/AdminNav.tsx b/src/app/(main)/admin/AdminNav.tsx new file mode 100644 index 000000000..20c011554 --- /dev/null +++ b/src/app/(main)/admin/AdminNav.tsx @@ -0,0 +1,48 @@ +import { SideMenu } from '@/components/common/SideMenu'; +import { useMessages, useNavigation } from '@/components/hooks'; +import { Globe, User, Users } from '@/components/icons'; + +export function AdminNav({ onItemClick }: { onItemClick?: () => void }) { + const { formatMessage, labels } = useMessages(); + const { pathname } = useNavigation(); + + const items = [ + { + label: formatMessage(labels.manage), + items: [ + { + id: 'users', + label: formatMessage(labels.users), + path: '/admin/users', + icon: , + }, + { + id: 'websites', + label: formatMessage(labels.websites), + path: '/admin/websites', + icon: , + }, + { + id: 'teams', + label: formatMessage(labels.teams), + path: '/admin/teams', + icon: , + }, + ], + }, + ]; + + const selectedKey = items + .flatMap(e => e.items) + ?.find(({ path }) => path && pathname.startsWith(path))?.id; + + return ( + + ); +} diff --git a/src/app/(main)/settings/SettingsLayout.tsx b/src/app/(main)/settings/SettingsLayout.tsx index 787d6c64c..fc7b11e75 100644 --- a/src/app/(main)/settings/SettingsLayout.tsx +++ b/src/app/(main)/settings/SettingsLayout.tsx @@ -1,50 +1,10 @@ 'use client'; import { PageBody } from '@/components/common/PageBody'; -import { SideMenu } from '@/components/common/SideMenu'; -import { useMessages, useNavigation } from '@/components/hooks'; -import { Settings2, UserCircle, Users } from '@/components/icons'; import { Column, Grid } from '@umami/react-zen'; import { ReactNode } from 'react'; +import { SettingsNav } from './SettingsNav'; export function SettingsLayout({ children }: { children: ReactNode }) { - const { formatMessage, labels } = useMessages(); - const { renderUrl, pathname } = useNavigation(); - - const items = [ - { - label: formatMessage(labels.application), - items: [ - { - id: 'preferences', - label: formatMessage(labels.preferences), - path: renderUrl('/settings/preferences'), - icon: , - }, - ], - }, - { - label: formatMessage(labels.account), - items: [ - { - id: 'profile', - label: formatMessage(labels.profile), - path: renderUrl('/settings/profile'), - icon: , - }, - { - id: 'teams', - label: formatMessage(labels.teams), - path: renderUrl('/settings/teams'), - icon: , - }, - ], - }, - ]; - - const selectedKey = items - .flatMap(e => e.items) - .find(({ path }) => path && pathname.includes(path.split('?')[0]))?.id; - return ( - + {children} diff --git a/src/app/(main)/settings/SettingsNav.tsx b/src/app/(main)/settings/SettingsNav.tsx new file mode 100644 index 000000000..4b35c82bd --- /dev/null +++ b/src/app/(main)/settings/SettingsNav.tsx @@ -0,0 +1,53 @@ +import { SideMenu } from '@/components/common/SideMenu'; +import { useMessages, useNavigation } from '@/components/hooks'; +import { Settings2, UserCircle, Users } from '@/components/icons'; + +export function SettingsNav({ onItemClick }: { onItemClick?: () => void }) { + const { formatMessage, labels } = useMessages(); + const { renderUrl, pathname } = useNavigation(); + + const items = [ + { + label: formatMessage(labels.application), + items: [ + { + id: 'preferences', + label: formatMessage(labels.preferences), + path: renderUrl('/settings/preferences'), + icon: , + }, + ], + }, + { + label: formatMessage(labels.account), + items: [ + { + id: 'profile', + label: formatMessage(labels.profile), + path: renderUrl('/settings/profile'), + icon: , + }, + { + id: 'teams', + label: formatMessage(labels.teams), + path: renderUrl('/settings/teams'), + icon: , + }, + ], + }, + ]; + + const selectedKey = items + .flatMap(e => e.items) + .find(({ path }) => path && pathname.includes(path.split('?')[0]))?.id; + + return ( + + ); +} From f073fb1996a637815d4296c8cd8bf8bb6d5e3839 Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Wed, 29 Oct 2025 12:38:52 -0700 Subject: [PATCH 5/8] Add DialogTrigger to overflow menus --- .../(reports)/breakdown/BreakdownPage.tsx | 44 +++++++++---------- .../[websiteId]/cohorts/CohortAddButton.tsx | 2 +- .../[websiteId]/cohorts/CohortEditButton.tsx | 2 +- .../[websiteId]/segments/SegmentAddButton.tsx | 1 + .../segments/SegmentEditButton.tsx | 1 + 5 files changed, 26 insertions(+), 24 deletions(-) diff --git a/src/app/(main)/websites/[websiteId]/(reports)/breakdown/BreakdownPage.tsx b/src/app/(main)/websites/[websiteId]/(reports)/breakdown/BreakdownPage.tsx index eff072fde..e73ce10ef 100644 --- a/src/app/(main)/websites/[websiteId]/(reports)/breakdown/BreakdownPage.tsx +++ b/src/app/(main)/websites/[websiteId]/(reports)/breakdown/BreakdownPage.tsx @@ -1,23 +1,27 @@ 'use client'; -import { useState } from 'react'; -import { Button, Column, Box, DialogTrigger, Popover, Dialog, IconLabel } from '@umami/react-zen'; -import { useDateRange, useMessages } from '@/components/hooks'; -import { ListCheck } from '@/components/icons'; -import { Panel } from '@/components/common/Panel'; -import { Breakdown } from './Breakdown'; -import { WebsiteControls } from '@/app/(main)/websites/[websiteId]/WebsiteControls'; import { FieldSelectForm } from '@/app/(main)/websites/[websiteId]/(reports)/breakdown/FieldSelectForm'; +import { WebsiteControls } from '@/app/(main)/websites/[websiteId]/WebsiteControls'; +import { Panel } from '@/components/common/Panel'; +import { useDateRange, useMessages, useMobile } from '@/components/hooks'; +import { ListCheck } from '@/components/icons'; +import { DialogButton } from '@/components/input/DialogButton'; +import { Column, Row } from '@umami/react-zen'; +import { useState } from 'react'; +import { Breakdown } from './Breakdown'; export function BreakdownPage({ websiteId }: { websiteId: string }) { const { dateRange: { startDate, endDate }, } = useDateRange(); const [fields, setFields] = useState(['path']); + const { isMobile } = useMobile(); return ( - + + + { const { formatMessage, labels } = useMessages(); return ( - - - - - - {({ close }) => ( - - )} - - - - + } + label={formatMessage(labels.fields)} + width="800px" + minHeight="300px" + > + {({ close }) => { + return ; + }} + ); }; diff --git a/src/app/(main)/websites/[websiteId]/cohorts/CohortAddButton.tsx b/src/app/(main)/websites/[websiteId]/cohorts/CohortAddButton.tsx index 53bab8a15..737ab502c 100644 --- a/src/app/(main)/websites/[websiteId]/cohorts/CohortAddButton.tsx +++ b/src/app/(main)/websites/[websiteId]/cohorts/CohortAddButton.tsx @@ -12,7 +12,7 @@ export function CohortAddButton({ websiteId }: { websiteId: string }) { label={formatMessage(labels.cohort)} variant="primary" width="800px" - minHeight="300px" + height="calc(100dvh - 40px)" > {({ close }) => { return ; diff --git a/src/app/(main)/websites/[websiteId]/cohorts/CohortEditButton.tsx b/src/app/(main)/websites/[websiteId]/cohorts/CohortEditButton.tsx index 693f31d77..48944677d 100644 --- a/src/app/(main)/websites/[websiteId]/cohorts/CohortEditButton.tsx +++ b/src/app/(main)/websites/[websiteId]/cohorts/CohortEditButton.tsx @@ -21,7 +21,7 @@ export function CohortEditButton({ variant="quiet" title={formatMessage(labels.cohort)} width="800px" - minHeight="300px" + height="calc(100dvh - 40px)" > {({ close }) => { return ( diff --git a/src/app/(main)/websites/[websiteId]/segments/SegmentAddButton.tsx b/src/app/(main)/websites/[websiteId]/segments/SegmentAddButton.tsx index 052d3185b..aed63ac68 100644 --- a/src/app/(main)/websites/[websiteId]/segments/SegmentAddButton.tsx +++ b/src/app/(main)/websites/[websiteId]/segments/SegmentAddButton.tsx @@ -12,6 +12,7 @@ export function SegmentAddButton({ websiteId }: { websiteId: string }) { label={formatMessage(labels.segment)} variant="primary" width="800px" + height="calc(100dvh - 40px)" > {({ close }) => { return ; diff --git a/src/app/(main)/websites/[websiteId]/segments/SegmentEditButton.tsx b/src/app/(main)/websites/[websiteId]/segments/SegmentEditButton.tsx index 6d422c95b..8c025772b 100644 --- a/src/app/(main)/websites/[websiteId]/segments/SegmentEditButton.tsx +++ b/src/app/(main)/websites/[websiteId]/segments/SegmentEditButton.tsx @@ -21,6 +21,7 @@ export function SegmentEditButton({ title={formatMessage(labels.segment)} variant="quiet" width="800px" + height="calc(100dvh - 40px)" > {({ close }) => { return ( From dfe969cabe03d783522ba19f72bf367873ea866c Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Thu, 30 Oct 2025 12:53:12 -0700 Subject: [PATCH 6/8] Fixed pixels/links collect. --- src/app/api/send/route.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/app/api/send/route.ts b/src/app/api/send/route.ts index 3e5699889..f5e00c8aa 100644 --- a/src/app/api/send/route.ts +++ b/src/app/api/send/route.ts @@ -82,6 +82,8 @@ export async function POST(request: Request) { id, } = payload; + const sourceId = websiteId || pixelId || linkId; + // Cache check let cache: Cache | null = null; @@ -128,13 +130,13 @@ export async function POST(request: Request) { const sessionSalt = hash(startOfMonth(createdAt).toUTCString()); const visitSalt = hash(startOfHour(createdAt).toUTCString()); - const sessionId = id ? uuid(websiteId, id) : uuid(websiteId, ip, userAgent, sessionSalt); + const sessionId = id ? uuid(sourceId, id) : uuid(sourceId, ip, userAgent, sessionSalt); // Create a session if not found if (!clickhouse.enabled && !cache?.sessionId) { await createSession({ id: sessionId, - websiteId, + websiteId: sourceId, browser, os, device, @@ -206,7 +208,7 @@ export async function POST(request: Request) { : EVENT_TYPE.pageView; await saveEvent({ - websiteId: websiteId || linkId || pixelId, + websiteId: sourceId, sessionId, visitId, eventType, @@ -270,6 +272,9 @@ export async function POST(request: Request) { } catch (e) { const error = serializeError(e); + // eslint-disable-next-line no-console + console.log(error); + return serverError({ errorObject: error }); } } From 504c45909072dbb2e6d0c0bdd93160b0f14f0d6e Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Thu, 30 Oct 2025 16:13:36 -0700 Subject: [PATCH 7/8] update schema validation for link/pixel updates --- src/app/api/links/[linkId]/route.ts | 6 +++--- src/app/api/pixels/[pixelId]/route.ts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/app/api/links/[linkId]/route.ts b/src/app/api/links/[linkId]/route.ts index cd93b9c64..512f39c96 100644 --- a/src/app/api/links/[linkId]/route.ts +++ b/src/app/api/links/[linkId]/route.ts @@ -24,9 +24,9 @@ export async function GET(request: Request, { params }: { params: Promise<{ link export async function POST(request: Request, { params }: { params: Promise<{ linkId: string }> }) { const schema = z.object({ - name: z.string(), - url: z.string(), - slug: z.string(), + name: z.string().optional(), + url: z.string().optional(), + slug: z.string().min(8).optional(), }); const { auth, body, error } = await parseRequest(request, schema); diff --git a/src/app/api/pixels/[pixelId]/route.ts b/src/app/api/pixels/[pixelId]/route.ts index 0788579f1..2f547c04f 100644 --- a/src/app/api/pixels/[pixelId]/route.ts +++ b/src/app/api/pixels/[pixelId]/route.ts @@ -24,8 +24,8 @@ export async function GET(request: Request, { params }: { params: Promise<{ pixe export async function POST(request: Request, { params }: { params: Promise<{ pixelId: string }> }) { const schema = z.object({ - name: z.string(), - slug: z.string().min(8), + name: z.string().optional(), + slug: z.string().min(8).optional(), }); const { auth, body, error } = await parseRequest(request, schema); From b08a6e11138c41956836015e4de4caa74331c46e Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Thu, 30 Oct 2025 17:05:11 -0700 Subject: [PATCH 8/8] Don't prefetch pixel links. --- src/app/(main)/pixels/[pixelId]/PixelHeader.tsx | 2 +- src/components/common/LinkButton.tsx | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/app/(main)/pixels/[pixelId]/PixelHeader.tsx b/src/app/(main)/pixels/[pixelId]/PixelHeader.tsx index 7e1a62b87..a49729910 100644 --- a/src/app/(main)/pixels/[pixelId]/PixelHeader.tsx +++ b/src/app/(main)/pixels/[pixelId]/PixelHeader.tsx @@ -11,7 +11,7 @@ export function PixelHeader() { return ( } marginBottom="3"> - + diff --git a/src/components/common/LinkButton.tsx b/src/components/common/LinkButton.tsx index ca1baccbc..251f77626 100644 --- a/src/components/common/LinkButton.tsx +++ b/src/components/common/LinkButton.tsx @@ -8,6 +8,7 @@ export interface LinkButtonProps extends ButtonProps { target?: string; scroll?: boolean; variant?: any; + prefetch?: boolean; children?: ReactNode; } @@ -16,6 +17,7 @@ export function LinkButton({ variant, scroll = true, target, + prefetch, children, ...props }: LinkButtonProps) { @@ -23,7 +25,7 @@ export function LinkButton({ return (