web: filter events by name (all events and profile events)

This commit is contained in:
Carl-Gerhard Lindesvärd
2023-12-13 11:08:10 +01:00
parent 13d7ad2a8c
commit 0a15a773e2
7 changed files with 93 additions and 45 deletions

View File

@@ -27,8 +27,8 @@ As of today (2023-12-12) I have more then 1.2 million events in PSQL and perform
- [ ] Active users (5min, 10min, 30min)
- [x] Save report to a specific dashboard
- [x] View events in a list
- [ ] Simple filters
- [*] View profiles in a list
- [x] Simple filters
- [x] View profiles in a list
- [ ] Invite users
- [ ] Drag n Drop reports on dashboard
- [x] Manage dashboards
@@ -42,7 +42,7 @@ As of today (2023-12-12) I have more then 1.2 million events in PSQL and perform
### SDK
- [*] Store duration on screen view events (can be done in backend as well)
- [x] Store duration on screen view events (can be done in backend as well)
- [x] Create native sdk
- [x] Handle sessions
- [x] Create web sdk

View File

@@ -1,7 +1,5 @@
import { useMemo } from 'react';
import { DataTable } from '@/components/DataTable';
import { Pagination } from '@/components/Pagination';
import type { PaginationProps } from '@/components/Pagination';
import { Avatar, AvatarFallback } from '@/components/ui/avatar';
import { Table, TableBody, TableCell, TableRow } from '@/components/ui/table';
import { useOrganizationParams } from '@/hooks/useOrganizationParams';
@@ -17,10 +15,9 @@ const columnHelper =
interface EventsTableProps {
data: RouterOutputs['event']['list'];
pagination: PaginationProps;
}
export function EventsTable({ data, pagination }: EventsTableProps) {
export function EventsTable({ data }: EventsTableProps) {
const params = useOrganizationParams();
const columns = useMemo(() => {
return [
@@ -94,11 +91,5 @@ export function EventsTable({ data, pagination }: EventsTableProps) {
];
}, [params]);
return (
<>
<Pagination {...pagination} />
<DataTable data={data} columns={columns} />
<Pagination {...pagination} />
</>
);
return <DataTable data={data} columns={columns} />;
}

View File

@@ -5,7 +5,7 @@ import { Command, CommandGroup, CommandItem } from '@/components/ui/command';
import { Checkbox } from './checkbox';
import { Input } from './input';
type IValue = string | number | boolean | null;
type IValue = any;
type IItem = Record<'value' | 'label', IValue>;
interface ComboboxAdvancedProps {
@@ -60,7 +60,7 @@ export function ComboboxAdvanced({
);
};
const renderUnknownItem = (value: string | number | null | boolean) => {
const renderUnknownItem = (value: IValue) => {
const item = items.find((item) => item.value === value);
return item ? renderItem(item) : renderItem({ value, label: value });
};
@@ -92,7 +92,7 @@ export function ComboboxAdvanced({
</button>
<div className="relative mt-2">
{open && (
<div className="max-h-80 absolute w-full z-10 top-0 rounded-md border bg-popover text-popover-foreground shadow-md outline-none animate-in">
<div className="max-h-80 min-w-[300px] absolute w-full z-10 top-0 rounded-md border bg-popover text-popover-foreground shadow-md outline-none animate-in">
<CommandGroup className="max-h-80 overflow-auto">
<div className="p-1 mb-2">
<Input

View File

@@ -183,6 +183,7 @@ export const pushModal = <
) =>
emitter.emit('push', {
name,
// @ts-expect-error
props: Array.isArray(rest) && rest[0] ? rest[0] : {},
});
export const replaceModal = <
@@ -194,6 +195,7 @@ export const replaceModal = <
) =>
emitter.emit('replace', {
name,
// @ts-expect-error
props: Array.isArray(rest) && rest[0] ? rest[0] : {},
});
export const popModal = (name?: StateItem['name']) =>

View File

@@ -1,17 +1,20 @@
import { useMemo } from 'react';
import { useMemo, useState } from 'react';
import { Container } from '@/components/Container';
import { EventsTable } from '@/components/events/EventsTable';
import { MainLayout } from '@/components/layouts/MainLayout';
import { PageTitle } from '@/components/PageTitle';
import { usePagination } from '@/components/Pagination';
import { Pagination, usePagination } from '@/components/Pagination';
import { ComboboxAdvanced } from '@/components/ui/combobox-advanced';
import { useOrganizationParams } from '@/hooks/useOrganizationParams';
import { api } from '@/utils/api';
export default function Events() {
const pagination = usePagination();
const params = useOrganizationParams();
const [eventFilters, setEventFilters] = useState<string[]>([]);
const eventsQuery = api.event.list.useQuery(
{
events: eventFilters,
projectSlug: params.project,
...pagination,
},
@@ -21,11 +24,32 @@ export default function Events() {
);
const events = useMemo(() => eventsQuery.data ?? [], [eventsQuery]);
const filterEventsQuery = api.chart.events.useQuery({
projectSlug: params.project,
});
const filterEvents = (filterEventsQuery.data ?? []).map((item) => ({
value: item.name,
label: item.name,
}));
return (
<MainLayout>
<Container>
<PageTitle>Events</PageTitle>
<EventsTable data={events} pagination={pagination} />
<div className="flex justify-between items-center">
<div>
<ComboboxAdvanced
items={filterEvents}
selected={eventFilters}
setSelected={setEventFilters}
placeholder="Filter by event"
/>
</div>
<Pagination {...pagination} />
</div>
<EventsTable data={events} />
<Pagination {...pagination} />
</Container>
</MainLayout>
);

View File

@@ -1,12 +1,12 @@
import { useMemo } from 'react';
import { useMemo, useState } from 'react';
import { Container } from '@/components/Container';
import { EventsTable } from '@/components/events/EventsTable';
import { MainLayout } from '@/components/layouts/MainLayout';
import { PageTitle } from '@/components/PageTitle';
import { usePagination } from '@/components/Pagination';
import { Pagination, usePagination } from '@/components/Pagination';
import { ComboboxAdvanced } from '@/components/ui/combobox-advanced';
import { useOrganizationParams } from '@/hooks/useOrganizationParams';
import { useQueryParams } from '@/hooks/useQueryParams';
import { createServerSideProps } from '@/server/getServerSideProps';
import { api } from '@/utils/api';
import { getProfileName } from '@/utils/getters';
import { z } from 'zod';
@@ -19,6 +19,14 @@ export default function ProfileId() {
profileId: z.string(),
})
);
const [eventFilters, setEventFilters] = useState<string[]>([]);
const filterEventsQuery = api.chart.events.useQuery({
projectSlug: params.project,
});
const filterEvents = (filterEventsQuery.data ?? []).map((item) => ({
value: item.name,
label: item.name,
}));
const profileQuery = api.profile.get.useQuery({
id: profileId,
});
@@ -26,6 +34,7 @@ export default function ProfileId() {
{
projectSlug: params.project,
profileId,
events: eventFilters,
...pagination,
},
{
@@ -40,7 +49,19 @@ export default function ProfileId() {
<Container>
<PageTitle>{getProfileName(profile)}</PageTitle>
<pre>{JSON.stringify(profile?.properties, null, 2)}</pre>
<EventsTable data={events} pagination={pagination} />
<div className="flex justify-between items-center">
<div>
<ComboboxAdvanced
items={filterEvents}
selected={eventFilters}
setSelected={setEventFilters}
placeholder="Filter by event"
/>
</div>
<Pagination {...pagination} />
</div>
<EventsTable data={events} />
<Pagination {...pagination} />
</Container>
</MainLayout>
);

View File

@@ -16,27 +16,37 @@ export const eventRouter = createTRPCRouter({
take: z.number().default(100),
skip: z.number().default(0),
profileId: z.string().optional(),
events: z.array(z.string()).optional(),
})
)
.query(async ({ input: { take, skip, projectSlug, profileId } }) => {
const project = await db.project.findUniqueOrThrow({
where: {
slug: projectSlug,
},
});
return db.event.findMany({
take,
skip,
where: {
project_id: project.id,
profile_id: profileId,
},
orderBy: {
createdAt: 'desc',
},
include: {
profile: true,
},
});
}),
.query(
async ({ input: { take, skip, projectSlug, profileId, events } }) => {
const project = await db.project.findUniqueOrThrow({
where: {
slug: projectSlug,
},
});
return db.event.findMany({
take,
skip,
where: {
project_id: project.id,
profile_id: profileId,
...(events && events.length > 0
? {
name: {
in: events,
},
}
: {}),
},
orderBy: {
createdAt: 'desc',
},
include: {
profile: true,
},
});
}
),
});