From f6c6a403b4d55da7c4314eaa2db14565f1125187 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carl-Gerhard=20Lindesva=CC=88rd?= Date: Thu, 15 Feb 2024 00:35:13 +0100 Subject: [PATCH] wip event list item --- .../[projectId]/events/event-list-item.tsx | 224 +++++++++++++++--- .../[projectId]/events/list-events.tsx | 22 +- .../components/general/ExpandableListItem.tsx | 16 +- apps/web/src/components/ui/key-value.tsx | 51 ++++ .../migration.sql | 10 + packages/db/prisma/schema.prisma | 10 + 6 files changed, 277 insertions(+), 56 deletions(-) create mode 100644 apps/web/src/components/ui/key-value.tsx create mode 100644 packages/db/prisma/migrations/20240214222026_add_event_meta/migration.sql diff --git a/apps/web/src/app/(app)/[organizationId]/[projectId]/events/event-list-item.tsx b/apps/web/src/app/(app)/[organizationId]/[projectId]/events/event-list-item.tsx index 5d3f1038..e2b337ec 100644 --- a/apps/web/src/app/(app)/[organizationId]/[projectId]/events/event-list-item.tsx +++ b/apps/web/src/app/(app)/[organizationId]/[projectId]/events/event-list-item.tsx @@ -1,15 +1,12 @@ 'use client'; -import { useMemo } from 'react'; import type { RouterOutputs } from '@/app/_trpc/client'; -import { ListProperties } from '@/components/events/ListProperties'; import { ExpandableListItem } from '@/components/general/ExpandableListItem'; -import { ProfileAvatar } from '@/components/profiles/ProfileAvatar'; +import { KeyValue, KeyValueSubtle } from '@/components/ui/key-value'; import { useAppParams } from '@/hooks/useAppParams'; -import { formatDateTime } from '@/utils/date'; import { getProfileName } from '@/utils/getters'; import { round } from '@/utils/math'; -import Link from 'next/link'; +import { useQueryState } from 'nuqs'; import { EventIcon } from './event-icon'; @@ -21,49 +18,202 @@ export function EventListItem({ name, properties, path, + duration, + referrer, + referrerName, + referrerType, + brand, + model, + browser, + browserVersion, + os, + osVersion, + city, + region, + country, + continent, + device, }: EventListItemProps) { const params = useAppParams(); - const bullets = useMemo(() => { - const bullets: React.ReactNode[] = [ - {formatDateTime(createdAt)}, - ]; + const [, setPath] = useQueryState('path'); + const [, setReferrer] = useQueryState('referrer'); + const [, setReferrerName] = useQueryState('referrerName'); + const [, setReferrerType] = useQueryState('referrerType'); + const [, setBrand] = useQueryState('brand'); + const [, setModel] = useQueryState('model'); + const [, setBrowser] = useQueryState('browser'); + const [, setBrowserVersion] = useQueryState('browserVersion'); + const [, setOs] = useQueryState('os'); + const [, setOsVersion] = useQueryState('osVersion'); + const [, setCity] = useQueryState('city'); + const [, setRegion] = useQueryState('region'); + const [, setCountry] = useQueryState('country'); + const [, setContinent] = useQueryState('continent'); + const [, setDevice] = useQueryState('device'); - if (profile) { - bullets.push( - - - {getProfileName(profile)} - - ); - } + const keyValueList = [ + { + name: 'Duration', + value: duration ? round(duration / 1000, 1) : undefined, + }, + { + name: 'Referrer', + value: referrer, + onClick() { + setReferrer(referrer ?? null); + }, + }, + { + name: 'Referrer name', + value: referrerName, + onClick() { + setReferrerName(referrerName ?? null); + }, + }, + { + name: 'Referrer type', + value: referrerType, + onClick() { + setReferrerType(referrerType ?? null); + }, + }, + { + name: 'Brand', + value: brand, + onClick() { + setBrand(brand ?? null); + }, + }, + { + name: 'Model', + value: model, + onClick() { + setModel(model ?? null); + }, + }, + { + name: 'Browser', + value: browser, + onClick() { + setBrowser(browser ?? null); + }, + }, + { + name: 'Browser version', + value: browserVersion, + onClick() { + setBrowserVersion(browserVersion ?? null); + }, + }, + { + name: 'OS', + value: os, + onClick() { + setOs(os ?? null); + }, + }, + { + name: 'OS cersion', + value: osVersion, + onClick() { + setOsVersion(osVersion ?? null); + }, + }, + { + name: 'City', + value: city, + onClick() { + setCity(city ?? null); + }, + }, + { + name: 'Region', + value: region, + onClick() { + setRegion(region ?? null); + }, + }, + { + name: 'Country', + value: country, + onClick() { + setCountry(country ?? null); + }, + }, + { + name: 'Continent', + value: continent, + onClick() { + setContinent(continent ?? null); + }, + }, + { + name: 'Device', + value: device, + onClick() { + setDevice(device ?? null); + }, + }, + ].filter((item) => typeof item.value === 'string' && item.value); - if (typeof properties.duration === 'number') { - bullets.push(`${round(properties.duration / 1000, 1)}s`); - } - - switch (name) { - case 'screen_view': { - if (path) { - bullets.push(path); - } - break; - } - } - - return bullets; - }, [name, createdAt, profile, properties, params, path]); + const propertiesList = Object.entries(properties) + .map(([name, value]) => ({ + name, + value: value as string | number | undefined, + })) + .filter((item) => typeof item.value === 'string' && item.value); return ( + + {profile && ( + } + value={getProfileName(profile)} + href={`/${params.organizationId}/${params.projectId}/profiles/${profile.id}`} + /> + )} + {path && ( + { + setPath(path); + }} + /> + )} + + } image={} > - + {propertiesList.length > 0 && ( +
+
Your properties
+
+ {propertiesList.map((item) => ( + + ))} +
+
+ )} +
+
Properties
+
+ {keyValueList.map((item) => ( + + ))} +
+
); } diff --git a/apps/web/src/app/(app)/[organizationId]/[projectId]/events/list-events.tsx b/apps/web/src/app/(app)/[organizationId]/[projectId]/events/list-events.tsx index 9f6df1e4..866295cf 100644 --- a/apps/web/src/app/(app)/[organizationId]/[projectId]/events/list-events.tsx +++ b/apps/web/src/app/(app)/[organizationId]/[projectId]/events/list-events.tsx @@ -7,6 +7,7 @@ import { FullPageEmptyState } from '@/components/FullPageEmptyState'; import { Pagination, usePagination } from '@/components/Pagination'; import { ComboboxAdvanced } from '@/components/ui/combobox-advanced'; import { GanttChartIcon } from 'lucide-react'; +import { parseAsArrayOf, parseAsString, useQueryState } from 'nuqs'; import { EventListItem } from './event-list-item'; @@ -15,22 +16,21 @@ interface ListEventsProps { } export function ListEvents({ projectId }: ListEventsProps) { const pagination = usePagination(); - const [eventFilters, setEventFilters] = useState([]); - const eventsQuery = api.event.list.useQuery( - { - events: eventFilters, - projectId: projectId, - ...pagination, - }, - { - keepPreviousData: true, - } + const [eventFilters, setEventFilters] = useQueryState( + 'events', + parseAsArrayOf(parseAsString).withDefault([]) ); + const eventsQuery = api.event.list.useQuery({ + events: eventFilters, + projectId: projectId, + ...pagination, + }); const events = useMemo(() => eventsQuery.data ?? [], [eventsQuery]); const filterEventsQuery = api.chart.events.useQuery({ projectId: projectId, }); + const filterEvents = (filterEventsQuery.data ?? []).map((item) => ({ value: item.name, label: item.name, @@ -61,7 +61,7 @@ export function ListEvents({ projectId }: ListEventsProps) { <>
{events.map((item) => ( - + ))}
diff --git a/apps/web/src/components/general/ExpandableListItem.tsx b/apps/web/src/components/general/ExpandableListItem.tsx index 0c40914e..1e33022c 100644 --- a/apps/web/src/components/general/ExpandableListItem.tsx +++ b/apps/web/src/components/general/ExpandableListItem.tsx @@ -7,14 +7,14 @@ import { Button } from '../ui/button'; interface ExpandableListItemProps { children: React.ReactNode; - bullets: React.ReactNode[]; + content: React.ReactNode; title: string; image?: React.ReactNode; initialOpen?: boolean; } export function ExpandableListItem({ title, - bullets, + content, image, initialOpen = false, children, @@ -22,15 +22,15 @@ export function ExpandableListItem({ const [open, setOpen] = useState(initialOpen ?? false); return (
-
+
{image}
{title} -
- {bullets.map((bullet, index) => ( - {bullet} - ))} -
+ {!!content && ( +
+ {content} +
+ )}