From e4b919c4dab3247465b5d29d2507a86d42a3438b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carl-Gerhard=20Lindesva=CC=88rd?= Date: Tue, 2 Dec 2025 21:54:57 +0100 Subject: [PATCH] fix: general ui fixes --- .../src/components/events/table/columns.tsx | 5 +- .../overview/overview-live-histogram.tsx | 8 +- .../realtime/realtime-active-sessions.tsx | 2 +- .../src/components/realtime/realtime-geo.tsx | 4 + .../realtime/realtime-live-histogram.tsx | 2 +- .../components/realtime/realtime-paths.tsx | 4 + .../realtime/realtime-referrals.tsx | 4 + .../src/components/report-chart/map/chart.tsx | 7 +- apps/start/src/modals/event-details.tsx | 466 +++++++++--------- ...pp.$organizationId.$projectId.realtime.tsx | 2 +- 10 files changed, 256 insertions(+), 248 deletions(-) diff --git a/apps/start/src/components/events/table/columns.tsx b/apps/start/src/components/events/table/columns.tsx index f801d221..a8ab6ba0 100644 --- a/apps/start/src/components/events/table/columns.tsx +++ b/apps/start/src/components/events/table/columns.tsx @@ -3,7 +3,6 @@ import { ProjectLink } from '@/components/links'; import { SerieIcon } from '@/components/report-chart/common/serie-icon'; import { useNumber } from '@/hooks/use-numer-formatter'; import { pushModal } from '@/modals'; -import { formatDateTime, formatTimeAgoOrDateTime, timeAgo } from '@/utils/date'; import { getProfileName } from '@/utils/getters'; import type { ColumnDef } from '@tanstack/react-table'; @@ -28,7 +27,7 @@ export function useColumns() { accessorKey: 'name', header: 'Name', cell({ row }) { - const { name, path, duration } = row.original; + const { name, path, duration, properties } = row.original; const renderName = () => { if (name === 'screen_view') { if (path.includes('/')) { @@ -209,7 +208,7 @@ export function useColumns() { ), ); const items = Object.entries(filteredProperties); - const limit = 1; + const limit = 2; const data = items.slice(0, limit).map(([key, value]) => ({ name: key, value: value, diff --git a/apps/start/src/components/overview/overview-live-histogram.tsx b/apps/start/src/components/overview/overview-live-histogram.tsx index f49aeede..5bc231d9 100644 --- a/apps/start/src/components/overview/overview-live-histogram.tsx +++ b/apps/start/src/components/overview/overview-live-histogram.tsx @@ -57,7 +57,7 @@ export function OverviewLiveHistogram({ +
{liveData.referrers.slice(0, 3).map((ref, index) => (
-
-
+
+
{count} sessions last 30 min
-
{icons}
+ {icons}
{children} diff --git a/apps/start/src/components/realtime/realtime-active-sessions.tsx b/apps/start/src/components/realtime/realtime-active-sessions.tsx index 21e0f44c..3a6db1e9 100644 --- a/apps/start/src/components/realtime/realtime-active-sessions.tsx +++ b/apps/start/src/components/realtime/realtime-active-sessions.tsx @@ -53,7 +53,7 @@ export function RealtimeActiveSessions({ const sessions = state.length > 0 ? state : (activeSessionsQuery.data ?? []); return ( -
+
diff --git a/apps/start/src/components/realtime/realtime-geo.tsx b/apps/start/src/components/realtime/realtime-geo.tsx index f10d485d..e09f1c85 100644 --- a/apps/start/src/components/realtime/realtime-geo.tsx +++ b/apps/start/src/components/realtime/realtime-geo.tsx @@ -51,6 +51,7 @@ export function RealtimeGeo({ projectId }: RealtimeGeoProps) { { name: 'Country / City', width: 'w-full', + responsive: { priority: 1 }, render(item) { return ( @@ -89,6 +92,7 @@ export function RealtimeGeo({ projectId }: RealtimeGeoProps) { { name: 'Sessions', width: '82px', + responsive: { priority: 2 }, render(item) { return (
diff --git a/apps/start/src/components/realtime/realtime-live-histogram.tsx b/apps/start/src/components/realtime/realtime-live-histogram.tsx index a6c8fece..cd97312c 100644 --- a/apps/start/src/components/realtime/realtime-live-histogram.tsx +++ b/apps/start/src/components/realtime/realtime-live-histogram.tsx @@ -58,7 +58,7 @@ export function RealtimeLiveHistogram({ count={totalVisitors} icons={ liveData.referrers && liveData.referrers.length > 0 ? ( -
+
{liveData.referrers.slice(0, 3).map((ref, index) => (
@@ -96,6 +99,7 @@ export function RealtimePaths({ projectId }: RealtimePathsProps) { { name: 'Sessions', width: '82px', + responsive: { priority: 2 }, render(item) { return (
diff --git a/apps/start/src/components/realtime/realtime-referrals.tsx b/apps/start/src/components/realtime/realtime-referrals.tsx index 1c7e7e97..b3881f1b 100644 --- a/apps/start/src/components/realtime/realtime-referrals.tsx +++ b/apps/start/src/components/realtime/realtime-referrals.tsx @@ -45,6 +45,7 @@ export function RealtimeReferrals({ projectId }: RealtimeReferralsProps) { { name: 'Referrer', width: 'w-full', + responsive: { priority: 1 }, render(item) { return ( @@ -59,6 +60,7 @@ export function RealtimeReferrals({ projectId }: RealtimeReferralsProps) { { name: 'Duration', width: '75px', + responsive: { priority: 7 }, render(item) { return number.shortWithUnit(item.avg_duration, 'min'); }, @@ -66,6 +68,7 @@ export function RealtimeReferrals({ projectId }: RealtimeReferralsProps) { { name: 'Events', width: '60px', + responsive: { priority: 4 }, render(item) { return (
@@ -79,6 +82,7 @@ export function RealtimeReferrals({ projectId }: RealtimeReferralsProps) { { name: 'Sessions', width: '82px', + responsive: { priority: 2 }, render(item) { return (
diff --git a/apps/start/src/components/report-chart/map/chart.tsx b/apps/start/src/components/report-chart/map/chart.tsx index 18d3932f..d1eb8c69 100644 --- a/apps/start/src/components/report-chart/map/chart.tsx +++ b/apps/start/src/components/report-chart/map/chart.tsx @@ -15,13 +15,12 @@ export function Chart({ data }: Props) { const { report: { metric, unit }, } = useReportChartContext(); - const { series } = useVisibleSeries(data, 100); - const [filters, setFilter] = useEventQueryFilters(); - + const { series } = useVisibleSeries(data, 99999); + const [_, setFilter] = useEventQueryFilters(); const mapData = useMemo( () => series.map((s) => ({ - country: s.names[0]?.toLowerCase() ?? '', + country: s.names[1]?.toLowerCase() ?? '', value: s.metrics[metric] ?? 0, })), [series, metric], diff --git a/apps/start/src/modals/event-details.tsx b/apps/start/src/modals/event-details.tsx index 83962221..1dbb3c05 100644 --- a/apps/start/src/modals/event-details.tsx +++ b/apps/start/src/modals/event-details.tsx @@ -18,10 +18,10 @@ import { useTRPC } from '@/integrations/trpc/react'; import { cn } from '@/utils/cn'; import { getProfileName } from '@/utils/getters'; import type { IClickhouseEvent, IServiceEvent } from '@openpanel/db'; -import { useQuery } from '@tanstack/react-query'; +import { useSuspenseQuery } from '@tanstack/react-query'; import { FilterIcon, XIcon } from 'lucide-react'; import { omit } from 'ramda'; -import { useState } from 'react'; +import { Suspense, useState } from 'react'; import { popModal } from '.'; import { ModalContent } from './Modal/Container'; @@ -52,7 +52,19 @@ const filterable: Partial> = origin: 'origin', }; -export default function EventDetails({ id, createdAt, projectId }: Props) { +export default function EventDetails(props: Props) { + return ( + + + }> + + + + + ); +} + +function EventDetailsContent({ id, createdAt, projectId }: Props) { const [, setEvents] = useEventQueryNamesFilter(); const [, setFilter] = useEventQueryFilters(); const TABS = { @@ -67,7 +79,7 @@ export default function EventDetails({ id, createdAt, projectId }: Props) { }; const [widget, setWidget] = useState(TABS.essentials); const trpc = useTRPC(); - const query = useQuery( + const query = useSuspenseQuery( trpc.event.details.queryOptions({ id, projectId, @@ -75,10 +87,6 @@ export default function EventDetails({ id, createdAt, projectId }: Props) { }), ); - if (!query.data) { - return ; - } - const { event, session } = query.data; const profile = event.profile; @@ -172,13 +180,12 @@ export default function EventDetails({ id, createdAt, projectId }: Props) { })); return ( - - - -
-
{event.name}
-
- {/* */} - -
+
+
- - {Object.entries(TABS).map(([, tab]) => ( - - ))} - - - - {profile && ( - popModal()} - href={`/profiles/${profile.id}`} - className="card p-4 py-2 col gap-2 hover:bg-def-100" + + {Object.entries(TABS).map(([, tab]) => ( + + ))} + + + + {profile && ( + popModal()} + href={`/profiles/${profile.id}`} + className="card p-4 py-2 col gap-2 hover:bg-def-100" + > +
+
+ {profile.avatar && ( + )} - onItemClick={(item) => { - popModal(); - setFilter(`properties.${item.name}`, item.value as any); - }} - /> - - )} +
+ {getProfileName(profile, false)} +
+
+
+
+ + + +
+
+ {event.referrerName || event.referrer} +
+
+
+ {!!session && ( +
+ This session has {session.screenViewCount} screen views and{' '} + {session.eventCount} events. Visit duration is{' '} + {fancyMinutes(session.duration / 1000)}. +
+ )} +
+ )} + + {properties.length > 0 && (
-
Information
+
Properties
{ - const isFilterable = - item.value && (filterable as any)[item.name]; - if (isFilterable) { - return ( -
- - -
- ); - } - - return ( - - ); - }} + data={properties} + renderValue={(item) => ( +
+ {String(item.value)} + +
+ )} onItemClick={(item) => { - const isFilterable = - item.value && (filterable as any)[item.name]; - if (isFilterable) { - popModal(); - setFilter(item.name as keyof IServiceEvent, item.value); - } + popModal(); + setFilter(`properties.${item.name}`, item.value as any); }} />
-
-
-
All events for {event.name}
- -
-
- -
-
-
- - + )} +
+
+
Information
+
+ { + const isFilterable = item.value && (filterable as any)[item.name]; + if (isFilterable) { + return ( +
+ + +
+ ); + } + + return ( + + ); + }} + onItemClick={(item) => { + const isFilterable = item.value && (filterable as any)[item.name]; + if (isFilterable) { + popModal(); + setFilter(item.name as keyof IServiceEvent, item.value); + } + }} + /> +
+
+
+
All events for {event.name}
+ +
+
+ +
+
+
+ ); } function EventDetailsSkeleton() { return ( - - - -
-
-
-
-
-
+ <> + +
+
+
+
+
+
+
+
+ + +
+
+ + + + {/* Profile skeleton */} +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
- -
-
- - - - {/* Profile skeleton */} -
-
-
-
+ {/* Properties skeleton */} +
+
+
+
+
+ {Array.from({ length: 3 }).map((_, i) => ( +
-
-
-
-
-
-
-
-
-
+ ))}
+
- {/* Properties skeleton */} -
-
-
-
-
- {Array.from({ length: 3 }).map((_, i) => ( -
-
-
-
- ))} -
-
+ {/* Information skeleton */} +
+
+
+
+
+ {Array.from({ length: 6 }).map((_, i) => ( +
+
+
+
+ ))} +
+
- {/* Information skeleton */} -
-
-
-
-
- {Array.from({ length: 6 }).map((_, i) => ( -
-
-
-
- ))} -
-
- - {/* Chart skeleton */} -
-
-
-
-
-
-
-
-
- - - + {/* Chart skeleton */} +
+
+
+
+
+
+
+
+
+ + ); } diff --git a/apps/start/src/routes/_app.$organizationId.$projectId.realtime.tsx b/apps/start/src/routes/_app.$organizationId.$projectId.realtime.tsx index 62bf3c62..fc7b2e8c 100644 --- a/apps/start/src/routes/_app.$organizationId.$projectId.realtime.tsx +++ b/apps/start/src/routes/_app.$organizationId.$projectId.realtime.tsx @@ -67,7 +67,7 @@ function Component() {
-
+