migrate to app dir and ssr

This commit is contained in:
Carl-Gerhard Lindesvärd
2024-01-20 22:54:38 +01:00
parent 719a82f1c4
commit 308ae98472
194 changed files with 4706 additions and 2194 deletions

View File

@@ -0,0 +1,46 @@
import { cn } from '@/utils/cn';
import type { VariantProps } from 'class-variance-authority';
import { cva } from 'class-variance-authority';
import { ActivityIcon, BotIcon, MonitorPlayIcon } from 'lucide-react';
const variants = cva('flex items-center justify-center shrink-0', {
variants: {
size: {
sm: 'w-6 h-6 rounded',
default: 'w-12 h-12 rounded-xl',
},
},
defaultVariants: {
size: 'default',
},
});
type EventIconProps = VariantProps<typeof variants> & {
name: string;
className?: string;
};
const records = {
default: { Icon: BotIcon, text: 'text-chart-0', bg: 'bg-chart-0/10' },
screen_view: {
Icon: MonitorPlayIcon,
text: 'text-chart-3',
bg: 'bg-chart-3/10',
},
session_start: {
Icon: ActivityIcon,
text: 'text-chart-2',
bg: 'bg-chart-2/10',
},
};
export function EventIcon({ className, name, size }: EventIconProps) {
const { Icon, text, bg } =
name in records ? records[name as keyof typeof records] : records.default;
return (
<div className={cn(variants({ size }), bg, className)}>
<Icon size={20} className={text} />
</div>
);
}

View File

@@ -0,0 +1,71 @@
'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 { useAppParams } from '@/hooks/useAppParams';
import { cn } from '@/utils/cn';
import { formatDateTime } from '@/utils/date';
import { getProfileName } from '@/utils/getters';
import { round } from '@/utils/math';
import { Activity, BotIcon, MonitorPlay } from 'lucide-react';
import Link from 'next/link';
import { EventIcon } from './event-icon';
type EventListItemProps = RouterOutputs['event']['list'][number];
export function EventListItem({
profile,
createdAt,
name,
properties,
}: EventListItemProps) {
const params = useAppParams();
const bullets = useMemo(() => {
const bullets: React.ReactNode[] = [
<span>{formatDateTime(createdAt)}</span>,
];
if (profile) {
bullets.push(
<Link
href={`/${params.organizationId}/${params.projectId}/profiles/${profile.id}`}
className="flex items-center gap-1 text-black font-medium hover:underline"
>
<ProfileAvatar size="xs" {...(profile ?? {})}></ProfileAvatar>
{getProfileName(profile)}
</Link>
);
}
if (typeof properties.duration === 'number') {
bullets.push(`${round(properties.duration / 1000, 1)}s`);
}
switch (name) {
case 'screen_view': {
const route = (properties?.route || properties?.path) as string;
if (route) {
bullets.push(route);
}
break;
}
}
return bullets;
}, [name, createdAt, profile, properties, params]);
return (
<ExpandableListItem
title={name.split('_').join(' ')}
bullets={bullets}
image={<EventIcon name={name} />}
>
<ListProperties data={properties} className="rounded-none border-none" />
</ExpandableListItem>
);
}

View File

@@ -0,0 +1,61 @@
'use client';
import { useMemo, useState } from 'react';
import { api } from '@/app/_trpc/client';
import { StickyBelowHeader } from '@/app/(app)/layout-sticky-below-header';
import { Pagination, usePagination } from '@/components/Pagination';
import { ComboboxAdvanced } from '@/components/ui/combobox-advanced';
import { EventListItem } from './event-list-item';
interface ListEventsProps {
projectId: string;
}
export function ListEvents({ projectId }: ListEventsProps) {
const pagination = usePagination();
const [eventFilters, setEventFilters] = useState<string[]>([]);
const eventsQuery = api.event.list.useQuery(
{
events: eventFilters,
projectId: projectId,
...pagination,
},
{
keepPreviousData: true,
}
);
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,
}));
return (
<>
<StickyBelowHeader className="p-4 flex justify-between">
<div>
<ComboboxAdvanced
items={filterEvents}
value={eventFilters}
onChange={setEventFilters}
placeholder="Filter by event"
/>
</div>
</StickyBelowHeader>
<div className="p-4">
<div className="flex flex-col gap-4">
{events.map((item) => (
<EventListItem key={item.id} {...item} />
))}
</div>
<div className="mt-2">
<Pagination {...pagination} />
</div>
</div>
</>
);
}

View File

@@ -0,0 +1,19 @@
import PageLayout from '@/app/(app)/page-layout';
import { ListEvents } from './list-events';
interface PageProps {
params: {
organizationId: string;
projectId: string;
};
}
export default function Page({
params: { organizationId, projectId },
}: PageProps) {
return (
<PageLayout title="Events" organizationId={organizationId}>
<ListEvents projectId={projectId} />
</PageLayout>
);
}