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

@@ -1,5 +1,5 @@
import { flatten, map, pipe, prop, range, sort, uniq } from 'ramda';
import { escape } from 'sqlstring';
import sqlstring from 'sqlstring';
import { z } from 'zod';
import {
@@ -52,6 +52,80 @@ function utc(date: string | Date) {
const cacher = cacheMiddleware(60);
export const chartRouter = createTRPCRouter({
projectCard: protectedProcedure
.use(cacheMiddleware(60 * 5))
.input(
z.object({
projectId: z.string(),
}),
)
.query(async ({ input: { projectId } }) => {
const chartPromise = chQuery<{ value: number; date: Date }>(
`SELECT
uniqHLL12(profile_id) as value,
toStartOfDay(created_at) as date
FROM ${TABLE_NAMES.sessions}
WHERE
sign = 1 AND
project_id = ${sqlstring.escape(projectId)} AND
created_at >= now() - interval '3 month'
GROUP BY date
ORDER BY date ASC
WITH FILL FROM toStartOfDay(now() - interval '1 month')
TO toStartOfDay(now())
STEP INTERVAL 1 day
`,
);
const metricsPromise = clix(ch)
.select<{
months_3: number;
months_3_prev: number;
month: number;
day: number;
day_prev: number;
}>([
'uniqHLL12(if(created_at >= (now() - toIntervalMonth(3)), profile_id, null)) AS months_3',
'uniqHLL12(if(created_at >= (now() - toIntervalMonth(6)) AND created_at < (now() - toIntervalMonth(3)), profile_id, null)) AS months_3_prev',
'uniqHLL12(if(created_at >= (now() - toIntervalMonth(1)), profile_id, null)) AS month',
'uniqHLL12(if(created_at >= (now() - toIntervalDay(1)), profile_id, null)) AS day',
'uniqHLL12(if(created_at >= (now() - toIntervalDay(2)) AND created_at < (now() - toIntervalDay(1)), profile_id, null)) AS day_prev',
])
.from(TABLE_NAMES.sessions)
.where('project_id', '=', projectId)
.where('created_at', '>=', clix.exp('now() - toIntervalMonth(6)'))
.execute();
const [chart, [metrics]] = await Promise.all([
chartPromise,
metricsPromise,
]);
const change =
metrics && metrics.months_3_prev > 0 && metrics.months_3 > 0
? Math.round(
((metrics.months_3 - metrics.months_3_prev) /
metrics.months_3_prev) *
100,
)
: null;
const trend =
change === null
? { direction: 'neutral' as const, percentage: null as number | null }
: change > 0
? { direction: 'up' as const, percentage: change }
: change < 0
? { direction: 'down' as const, percentage: Math.abs(change) }
: { direction: 'neutral' as const, percentage: 0 };
return {
chart: chart.map((d) => ({ ...d, date: new Date(d.date) })),
metrics,
trend,
};
}),
events: protectedProcedure
.input(
z.object({
@@ -61,7 +135,7 @@ export const chartRouter = createTRPCRouter({
.query(async ({ input: { projectId } }) => {
const [events, meta] = await Promise.all([
chQuery<{ name: string; count: number }>(
`SELECT name, count(name) as count FROM ${TABLE_NAMES.event_names_mv} WHERE project_id = ${escape(projectId)} GROUP BY name ORDER BY count DESC, name ASC`,
`SELECT name, count(name) as count FROM ${TABLE_NAMES.event_names_mv} WHERE project_id = ${sqlstring.escape(projectId)} GROUP BY name ORDER BY count DESC, name ASC`,
),
getEventMetasCached(projectId),
]);
@@ -376,9 +450,9 @@ export const chartRouter = createTRPCRouter({
const whereEventNameIs = (event: string[]) => {
if (event.length === 1) {
return `name = ${escape(event[0])}`;
return `name = ${sqlstring.escape(event[0])}`;
}
return `name IN (${event.map((e) => escape(e)).join(',')})`;
return `name IN (${event.map((e) => sqlstring.escape(e)).join(',')})`;
};
const cohortQuery = `
@@ -390,7 +464,7 @@ export const chartRouter = createTRPCRouter({
${sqlToStartOf}(created_at) AS cohort_interval
FROM ${TABLE_NAMES.cohort_events_mv}
WHERE ${whereEventNameIs(firstEvent)}
AND project_id = ${escape(projectId)}
AND project_id = ${sqlstring.escape(projectId)}
AND created_at BETWEEN toDate('${utc(dates.startDate)}') AND toDate('${utc(dates.endDate)}')
),
last_event AS
@@ -401,7 +475,7 @@ export const chartRouter = createTRPCRouter({
toDate(created_at) AS event_date
FROM cohort_events_mv
WHERE ${whereEventNameIs(secondEvent)}
AND project_id = ${escape(projectId)}
AND project_id = ${sqlstring.escape(projectId)}
AND created_at BETWEEN toDate('${utc(dates.startDate)}') AND toDate('${utc(dates.endDate)}') + INTERVAL ${diffInterval} ${sqlInterval}
),
retention_matrix AS