From c81b1c16c8de2cc1752e6d138b709c5e499d4d03 Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Wed, 29 Oct 2025 10:10:46 -0700 Subject: [PATCH 1/4] 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 2/4] 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 3/4] 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 4/4] 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 (