feat: add session count

This commit is contained in:
moonrailgun 2023-09-16 16:12:19 +08:00
parent 90166346e8
commit d5acf872a9
5 changed files with 77 additions and 38 deletions

View File

@ -140,5 +140,9 @@ export function useWorkspaceWebsitePageview(
}
);
return { stats: data?.stats ?? [], isLoading };
return {
pageviews: data?.pageviews ?? [],
sessions: data?.sessions ?? [],
isLoading,
};
}

View File

@ -47,7 +47,7 @@ const WebsiteOverviewItem: React.FC<{
const startDate = dayjs().subtract(1, 'day').add(1, unit).startOf(unit);
const endDate = dayjs().endOf(unit);
const { stats, isLoading } = useWorkspaceWebsitePageview(
const { pageviews, sessions, isLoading } = useWorkspaceWebsitePageview(
props.website.workspaceId,
props.website.id,
startDate.unix() * 1000,
@ -55,8 +55,14 @@ const WebsiteOverviewItem: React.FC<{
);
const chartData = useMemo(() => {
return getDateArray(stats, startDate, endDate, unit);
}, [stats, unit]);
const pageviewsArr = getDateArray(pageviews, startDate, endDate, unit);
const sessionsArr = getDateArray(sessions, startDate, endDate, unit);
return [
...pageviewsArr.map((item) => ({ ...item, type: 'pageview' })),
...sessionsArr.map((item) => ({ ...item, type: 'session' })),
];
}, [pageviews, sessions, unit]);
if (isLoading) {
return <Loading />;
@ -138,14 +144,16 @@ const MetricCard: React.FC<{
MetricCard.displayName = 'MetricCard';
export const StatsChart: React.FC<{
data: { x: string; y: number }[];
data: { x: string; y: number; type: string }[];
unit: DateUnit;
}> = React.memo((props) => {
const config: ColumnConfig = useMemo(
() => ({
data: props.data,
isStack: true,
xField: 'x',
yField: 'y',
seriesField: 'type',
label: {
position: 'middle' as const,
style: {

View File

@ -104,32 +104,6 @@ export async function deleteWorkspaceWebsite(
return website;
}
export async function getWorkspaceWebsitePageviewStats(
websiteId: string,
filters: QueryFilters
) {
const { timezone = 'utc', unit = 'day' } = filters;
const { filterQuery, joinSession, params } = await parseFilters(websiteId, {
...filters,
eventType: EVENT_TYPE.pageView,
});
return prisma.$queryRaw`
select
${getDateQuery('"WebsiteEvent"."createdAt"', unit, timezone)} x,
count(1) y
from "WebsiteEvent"
${joinSession ? Prisma.sql([joinSession]) : Prisma.empty}
where "WebsiteEvent"."websiteId" = ${params.websiteId}::uuid
and "WebsiteEvent"."createdAt" between ${
params.startDate
}::timestamptz and ${params.endDate}::timestamptz
and "WebsiteEvent"."eventType" = ${EVENT_TYPE.pageView}
${filterQuery}
group by 1
`;
}
export async function getWorkspaceWebsiteDateRange(websiteId: string) {
const { params } = await parseFilters(websiteId, {
startDate: new Date(DEFAULT_RESET_DATE),
@ -155,3 +129,53 @@ export async function getWorkspaceWebsiteDateRange(websiteId: string) {
min: res._min.createdAt,
};
}
export async function getWorkspaceWebsitePageviewStats(
websiteId: string,
filters: QueryFilters
) {
const { timezone = 'utc', unit = 'day' } = filters;
const { filterQuery, joinSession, params } = await parseFilters(websiteId, {
...filters,
});
return prisma.$queryRaw`
select
${getDateQuery('"WebsiteEvent"."createdAt"', unit, timezone)} x,
count(1) y
from "WebsiteEvent"
${joinSession}
where "WebsiteEvent"."websiteId" = ${params.websiteId}::uuid
and "WebsiteEvent"."createdAt" between ${
params.startDate
}::timestamptz and ${params.endDate}::timestamptz
and "WebsiteEvent"."eventType" = ${EVENT_TYPE.pageView}
${filterQuery}
group by 1
`;
}
export async function getWorkspaceWebsiteSessionStats(
websiteId: string,
filters: QueryFilters
) {
const { timezone = 'utc', unit = 'day' } = filters;
const { filterQuery, joinSession, params } = await parseFilters(websiteId, {
...filters,
});
return prisma.$queryRaw`
select
${getDateQuery('"WebsiteEvent"."createdAt"', unit, timezone)} x,
count(distinct "WebsiteEvent"."sessionId") y
from "WebsiteEvent"
${joinSession}
where "WebsiteEvent"."websiteId" = ${params.websiteId}::uuid
and "WebsiteEvent"."createdAt" between ${
params.startDate
}::timestamptz and ${params.endDate}::timestamptz
and "WebsiteEvent"."eventType" = ${EVENT_TYPE.pageView}
${filterQuery}
group by 1
`;
}

View File

@ -8,6 +8,7 @@ import {
getWorkspaceWebsiteInfo,
getWorkspaceWebsitePageviewStats,
getWorkspaceWebsites,
getWorkspaceWebsiteSessionStats,
updateWorkspaceWebsiteInfo,
} from '../model/workspace';
import { parseDateRange } from '../utils/common';
@ -186,11 +187,11 @@ workspaceRouter.get(
city,
};
const stats = await getWorkspaceWebsitePageviewStats(
websiteId,
filters as QueryFilters
);
const [pageviews, sessions] = await Promise.all([
getWorkspaceWebsitePageviewStats(websiteId, filters as QueryFilters),
getWorkspaceWebsiteSessionStats(websiteId, filters as QueryFilters),
]);
res.json({ stats });
res.json({ pageviews, sessions });
}
);

View File

@ -58,8 +58,10 @@ export async function parseFilters(
([key, value]) =>
typeof value !== 'undefined' && SESSION_COLUMNS.includes(key)
)
? `inner join "WebsiteSession" on "WebsiteEvent"."sessionId" = "WebsiteSession"."id"`
: '',
? Prisma.sql([
`inner join "WebsiteSession" on "WebsiteEvent"."sessionId" = "WebsiteSession"."id"`,
])
: Prisma.empty,
filterQuery: getFilterQuery(filters, options, websiteDomain),
params: {
...normalizeFilters(filters),