feat: dashboard v2, esm, upgrades (#211)

* esm

* wip

* wip

* wip

* wip

* wip

* wip

* subscription notice

* wip

* wip

* wip

* fix envs

* fix: update docker build

* fix

* esm/types

* delete dashboard :D

* add patches to dockerfiles

* update packages + catalogs + ts

* wip

* remove native libs

* ts

* improvements

* fix redirects and fetching session

* try fix favicon

* fixes

* fix

* order and resize reportds within a dashboard

* improvements

* wip

* added userjot to dashboard

* fix

* add op

* wip

* different cache key

* improve date picker

* fix table

* event details loading

* redo onboarding completely

* fix login

* fix

* fix

* extend session, billing and improve bars

* fix

* reduce price on 10M
This commit is contained in:
Carl-Gerhard Lindesvärd
2025-10-16 12:27:44 +02:00
committed by GitHub
parent 436e81ecc9
commit 81a7e5d62e
741 changed files with 32695 additions and 16996 deletions

View File

@@ -0,0 +1,184 @@
import { formatDateTime, formatTime } from '@/utils/date';
import type { ColumnDef } from '@tanstack/react-table';
import { isToday } from 'date-fns';
import { ProjectLink } from '@/components/links';
import { SerieIcon } from '@/components/report-chart/common/serie-icon';
import { createHeaderColumn } from '@/components/ui/data-table/data-table-helpers';
import type { RouterOutputs } from '@/trpc/client';
import type { INotificationPayload } from '@openpanel/db';
function getEventFromPayload(payload: INotificationPayload | null) {
if (payload?.type === 'event') {
return payload.event;
}
if (payload?.type === 'funnel') {
return payload.funnel[0] || null;
}
return null;
}
export function useColumns() {
const columns: ColumnDef<RouterOutputs['notification']['list'][number]>[] = [
{
accessorKey: 'title',
header: 'Title',
cell({ row }) {
const { title } = row.original;
return (
<div className="row gap-2 items-center">
{/* {isReadAt === null && <PingBadge>Unread</PingBadge>} */}
<span className="max-w-md truncate font-medium">{title}</span>
</div>
);
},
meta: {
variant: 'text',
placeholder: 'Search',
label: 'Title',
},
},
{
accessorKey: 'message',
header: 'Message',
cell({ row }) {
const { message } = row.original;
return (
<div className="inline-flex min-w-full flex-none items-center gap-2">
{message}
</div>
);
},
meta: {
label: 'Message',
hidden: true,
},
},
{
accessorKey: 'integration',
header: 'Integration',
cell({ row }) {
const integration = row.original.integration;
return <div>{integration?.name}</div>;
},
meta: {
label: 'Integration',
},
},
{
accessorKey: 'notificationRule',
header: 'Rule',
cell({ row }) {
const rule = row.original.notificationRule;
return <div>{rule?.name}</div>;
},
meta: {
label: 'Rule',
hidden: true,
},
},
{
accessorKey: 'country',
header: 'Country',
cell({ row }) {
const { payload } = row.original;
const event = getEventFromPayload(payload);
if (!event) {
return null;
}
return (
<div className="inline-flex min-w-full flex-none items-center gap-2">
<SerieIcon name={event.country} />
<span>{event.city}</span>
</div>
);
},
meta: {
label: 'Country',
},
},
{
accessorKey: 'os',
header: 'OS',
cell({ row }) {
const { payload } = row.original;
const event = getEventFromPayload(payload);
if (!event) {
return null;
}
return (
<div className="flex min-w-full items-center gap-2">
<SerieIcon name={event.os} />
<span>{event.os}</span>
</div>
);
},
meta: {
label: 'OS',
},
},
{
accessorKey: 'browser',
header: 'Browser',
cell({ row }) {
const { payload } = row.original;
const event = getEventFromPayload(payload);
if (!event) {
return null;
}
return (
<div className="inline-flex min-w-full flex-none items-center gap-2">
<SerieIcon name={event.browser} />
<span>{event.browser}</span>
</div>
);
},
meta: {
label: 'Browser',
},
},
{
accessorKey: 'profile',
header: createHeaderColumn('Profile'),
cell({ row }) {
const { payload } = row.original;
const event = getEventFromPayload(payload);
if (!event) {
return null;
}
return (
<ProjectLink
href={`/profiles/${event.profileId}`}
className="inline-flex min-w-full flex-none items-center gap-2"
>
{event.profileId}
</ProjectLink>
);
},
meta: {
label: 'Profile',
},
},
{
accessorKey: 'createdAt',
header: 'Created at',
cell({ row }) {
const date = row.original.createdAt;
if (!date) {
return null;
}
return (
<div>{isToday(date) ? formatTime(date) : formatDateTime(date)}</div>
);
},
filterFn: 'isWithinRange',
meta: {
variant: 'dateRange',
placeholder: 'Created at',
label: 'Created at',
},
},
];
return columns;
}