start refactor event list

This commit is contained in:
Carl-Gerhard Lindesvärd
2024-05-27 21:32:20 +02:00
parent 8d79949a18
commit 0e632dc493
21 changed files with 111 additions and 102 deletions

View File

@@ -1,4 +1,5 @@
import { ChartSwitchShortcut } from '@/components/report/chart';
import { Widget, WidgetBody, WidgetHead } from '@/components/widget';
import type { IChartEvent } from '@openpanel/validation';
@@ -20,23 +21,28 @@ export function EventsPerDayChart({ projectId, filters, events }: Props) {
];
return (
<div className="card mb-8 p-4">
<ChartSwitchShortcut
projectId={projectId}
range="30d"
chartType="histogram"
events={
events && events.length > 0
? events.map((name) => ({
id: name,
name,
displayName: name,
segment: 'event',
filters: filters ?? [],
}))
: fallback
}
/>
</div>
<Widget className="w-full">
<WidgetHead>
<span className="title">Events per day</span>
</WidgetHead>
<WidgetBody>
<ChartSwitchShortcut
projectId={projectId}
range="30d"
chartType="histogram"
events={
events && events.length > 0
? events.map((name) => ({
id: name,
name,
displayName: name,
segment: 'event',
filters: filters ?? [],
}))
: fallback
}
/>
</WidgetBody>
</Widget>
);
}

View File

@@ -6,7 +6,7 @@ import { isSameDay } from 'date-fns';
import type { IServiceCreateEventPayload } from '@openpanel/db';
import { EventListItem } from '../event-list-item';
import { EventListItem } from '../event-list/event-list-item';
function showDateHeader(a: Date, b?: Date) {
if (!b) return true;
@@ -18,7 +18,7 @@ interface EventListProps {
}
export function EventConversionsList({ data }: EventListProps) {
return (
<Widget>
<Widget className="w-full">
<WidgetHead>
<div className="title">Conversions</div>
</WidgetHead>

View File

@@ -1,3 +1,4 @@
import withLoadingWidget from '@/hocs/with-loading-widget';
import { escape } from 'sqlstring';
import { db, getEvents } from '@openpanel/db';
@@ -8,7 +9,7 @@ interface Props {
projectId: string;
}
export default async function EventConversionsListServer({ projectId }: Props) {
async function EventConversionsListServer({ projectId }: Props) {
const conversions = await db.eventMeta.findMany({
where: {
projectId,
@@ -30,3 +31,5 @@ export default async function EventConversionsListServer({ projectId }: Props) {
return <EventConversionsList data={events} />;
}
export default withLoadingWidget(EventConversionsListServer);

View File

@@ -1,5 +1,3 @@
'use client';
import { useState } from 'react';
import type { Dispatch, SetStateAction } from 'react';
import { ChartSwitchShortcut } from '@/components/report/chart';
@@ -27,6 +25,7 @@ interface Props {
open: boolean;
setOpen: Dispatch<SetStateAction<boolean>>;
}
export function EventDetails({ event, open, setOpen }: Props) {
const { name } = event;
const [isEditOpen, setIsEditOpen] = useState(false);

View File

@@ -1,25 +1,8 @@
import { useEffect, useState } from 'react';
import { Button } from '@/components/ui/button';
import { Checkbox } from '@/components/ui/checkbox';
import { Label } from '@/components/ui/label';
import {
Sheet,
SheetContent,
SheetFooter,
SheetHeader,
SheetTitle,
SheetTrigger,
} from '@/components/ui/sheet';
import { Tooltip, TooltipContent } from '@/components/ui/tooltip';
import { api } from '@/trpc/client';
import { cn } from '@/utils/cn';
import { TooltipTrigger } from '@radix-ui/react-tooltip';
import type { VariantProps } from 'class-variance-authority';
import { cva } from 'class-variance-authority';
import type { LucideIcon } from 'lucide-react';
import * as Icons from 'lucide-react';
import { useRouter } from 'next/navigation';
import { toast } from 'sonner';
import type { EventMeta } from '@openpanel/db';

View File

@@ -1,19 +1,13 @@
'use client';
import { Fragment, Suspense } from 'react';
import { Fragment } from 'react';
import { FullPageEmptyState } from '@/components/full-page-empty-state';
import { Pagination } from '@/components/pagination';
import { ChartSwitch, ChartSwitchShortcut } from '@/components/report/chart';
import { Button } from '@/components/ui/button';
import { useAppParams } from '@/hooks/useAppParams';
import { useCursor } from '@/hooks/useCursor';
import { useEventQueryFilters } from '@/hooks/useEventQueryFilters';
import { isSameDay } from 'date-fns';
import {
ChevronLeftIcon,
ChevronRightIcon,
GanttChartIcon,
} from 'lucide-react';
import { GanttChartIcon } from 'lucide-react';
import type { IServiceCreateEventPayload } from '@openpanel/db';
@@ -29,9 +23,11 @@ interface EventListProps {
data: IServiceCreateEventPayload[];
count: number;
}
export function EventList({ data, count }: EventListProps) {
function EventList({ data, count }: EventListProps) {
const { cursor, setCursor, loading } = useCursor();
const [filters] = useEventQueryFilters();
return (
<>
{data.length === 0 ? (
@@ -100,3 +96,5 @@ export function EventList({ data, count }: EventListProps) {
</>
);
}
export default EventList;

View File

@@ -0,0 +1,47 @@
import withSuspense from '@/hocs/with-suspense';
import { getEventList, getEventsCount } from '@openpanel/db';
import type { IChartEventFilter } from '@openpanel/validation';
import EventList from './event-list';
type Props = {
cursor?: number;
projectId: string;
filters?: IChartEventFilter[];
eventNames?: string[];
};
const EventListServer = async ({
cursor,
projectId,
eventNames,
filters,
}: Props) => {
const [events, count] = await Promise.all([
getEventList({
cursor,
projectId,
take: 50,
events: eventNames,
filters,
}),
getEventsCount({
projectId,
events: eventNames,
filters,
}),
]);
return <EventList data={events} count={count} />;
};
export default withSuspense(EventListServer, () => (
<div className="flex flex-col gap-2">
<div className="card h-[74px] w-full animate-pulse items-center justify-between rounded-lg p-4"></div>
<div className="card h-[74px] w-full animate-pulse items-center justify-between rounded-lg p-4"></div>
<div className="card h-[74px] w-full animate-pulse items-center justify-between rounded-lg p-4"></div>
<div className="card h-[74px] w-full animate-pulse items-center justify-between rounded-lg p-4"></div>
<div className="card h-[74px] w-full animate-pulse items-center justify-between rounded-lg p-4"></div>
</div>
));

View File

@@ -1,3 +0,0 @@
import FullPageLoadingState from '@/components/full-page-loading-state';
export default FullPageLoadingState;

View File

@@ -7,12 +7,10 @@ import {
} from '@/hooks/useEventQueryFilters';
import { parseAsInteger } from 'nuqs';
import { getEventList, getEventsCount } from '@openpanel/db';
import { StickyBelowHeader } from '../layout-sticky-below-header';
import { EventsPerDayChart } from './charts/events-per-day-chart';
import EventConversionsListServer from './event-conversions-list';
import { EventList } from './event-list';
import EventListServer from './event-list';
interface PageProps {
params: {
@@ -30,30 +28,16 @@ const nuqsOptions = {
shallow: false,
};
export default async function Page({
export default function Page({
params: { projectId, organizationSlug },
searchParams,
}: PageProps) {
const cursor =
parseAsInteger.parseServerSide(searchParams.cursor ?? '') ?? undefined;
const filters =
eventQueryFiltersParser.parseServerSide(searchParams.f ?? '') ?? undefined;
const eventsFilter = eventQueryNamesFilter.parseServerSide(
searchParams.events ?? ''
);
const [events, count] = await Promise.all([
getEventList({
cursor:
parseAsInteger.parseServerSide(searchParams.cursor ?? '') ?? undefined,
projectId,
take: 50,
events: eventsFilter,
filters,
}),
getEventsCount({
projectId,
events: eventsFilter,
filters,
}),
]);
const eventNames =
eventQueryNamesFilter.parseServerSide(searchParams.events) ?? undefined;
return (
<>
@@ -72,12 +56,17 @@ export default async function Page({
</StickyBelowHeader>
<div className="grid gap-4 p-4 md:grid-cols-2">
<div>
<EventList data={events} count={count} />
<EventListServer
projectId={projectId}
cursor={cursor}
filters={filters}
eventNames={eventNames}
/>
</div>
<div>
<div className="flex flex-col gap-4">
<EventsPerDayChart
projectId={projectId}
events={eventsFilter}
events={eventNames}
filters={filters}
/>
<EventConversionsListServer projectId={projectId} />

View File

@@ -20,7 +20,7 @@ async function ProfileTopServer({ organizationSlug, projectId }: Props) {
const res = await chQuery<{ profile_id: string; count: number }>(
`SELECT profile_id, count(*) as count from events where profile_id != '' and project_id = ${escape(projectId)} group by profile_id order by count() DESC LIMIT 50`
);
const profiles = await getProfiles({ ids: res.map((r) => r.profile_id) });
const profiles = await getProfiles(res.map((r) => r.profile_id));
const list = res.map((item) => {
return {
count: item.count,

View File

@@ -1,7 +1,7 @@
'use client';
import { useState } from 'react';
import { EventListItem } from '@/app/(app)/[organizationSlug]/[projectId]/events/event-list-item';
import { EventListItem } from '@/app/(app)/[organizationSlug]/[projectId]/events/event-list/event-list-item';
import useWS from '@/hooks/useWS';
import { AnimatePresence, motion } from 'framer-motion';

View File

@@ -1,7 +1,7 @@
'use client';
import { useState } from 'react';
import { EventListItem } from '@/app/(app)/[organizationSlug]/[projectId]/events/event-list-item';
import { EventListItem } from '@/app/(app)/[organizationSlug]/[projectId]/events/event-list/event-list-item';
import useWS from '@/hooks/useWS';
import { AnimatePresence, motion } from 'framer-motion';

View File

@@ -44,21 +44,12 @@ export function Pagination({
return (
<div
className={cn(
'flex select-none items-center justify-end gap-2',
'flex select-none items-center justify-end gap-1',
className
)}
>
{size === 'base' && (
<>
<div className="text-xs font-medium">Page: {cursor + 1}</div>
{typeof count === 'number' && (
<div className="text-xs font-medium">Total rows: {count}</div>
)}
</>
)}
{size === 'base' && (
<Button
loading={loading}
variant="outline"
size="icon"
onClick={() => setCursor(0)}
@@ -68,7 +59,6 @@ export function Pagination({
/>
)}
<Button
loading={loading}
variant="outline"
size="icon"
onClick={() => setCursor((p) => Math.max(0, p - 1))}
@@ -76,8 +66,11 @@ export function Pagination({
icon={ChevronLeftIcon}
/>
<Button loading={loading} disabled variant="outline" size="icon">
{loading ? '' : cursor}
</Button>
<Button
loading={loading}
variant="outline"
size="icon"
onClick={() => setCursor((p) => Math.min(lastCursor, p + 1))}
@@ -87,7 +80,6 @@ export function Pagination({
{size === 'base' && (
<Button
loading={loading}
variant="outline"
size="icon"
onClick={() => setCursor(lastCursor)}

View File

@@ -6,6 +6,7 @@ const withSuspense = <P,>(
) => {
const WithSuspense: React.ComponentType<P> = (props) => {
const fallback = <Fallback {...(props as any)} />;
// return <>{fallback}</>;
return (
<Suspense fallback={fallback}>
<Component {...(props as any)} />

View File

@@ -1,8 +1,5 @@
import { useCallback } from 'react';
// prettier-ignore
import type { Options as NuqsOptions } from 'nuqs';
import {
createParser,
parseAsArrayOf,