diff --git a/apps/web/src/app/(app)/[organizationId]/[projectId]/dashboards/[dashboardId]/page.tsx b/apps/web/src/app/(app)/[organizationId]/[projectId]/dashboards/[dashboardId]/page.tsx
index dc6d34f1..d2a27ba2 100644
--- a/apps/web/src/app/(app)/[organizationId]/[projectId]/dashboards/[dashboardId]/page.tsx
+++ b/apps/web/src/app/(app)/[organizationId]/[projectId]/dashboards/[dashboardId]/page.tsx
@@ -18,9 +18,9 @@ export default async function Page({
params: { organizationId, projectId, dashboardId },
}: PageProps) {
const [dashboard, reports] = await Promise.all([
- getDashboardById(dashboardId),
+ getDashboardById(dashboardId, projectId),
getReportsByDashboardId(dashboardId),
- getExists(organizationId, projectId),
+ getExists(organizationId),
]);
if (!dashboard) {
diff --git a/apps/web/src/app/(app)/[organizationId]/[projectId]/layout-project-selector.tsx b/apps/web/src/app/(app)/[organizationId]/[projectId]/layout-project-selector.tsx
index e56a397f..52a86986 100644
--- a/apps/web/src/app/(app)/[organizationId]/[projectId]/layout-project-selector.tsx
+++ b/apps/web/src/app/(app)/[organizationId]/[projectId]/layout-project-selector.tsx
@@ -23,12 +23,10 @@ export default function LayoutProjectSelector({
className="w-auto min-w-0 max-sm:max-w-[100px]"
placeholder={'Select project'}
onChange={(value) => {
- // If we are on a page with only organizationId and projectId (as params)
- // we know its safe to just replace the current projectId
- // since the rest of the url is to a static page
- // e.g. /[organizationId]/[projectId]/events
if (organizationId && projectId) {
- router.push(pathname.replace(projectId, value));
+ const split = pathname.replace(projectId, value).split('/');
+ // slicing here will remove everything after /{orgId}/{projectId}/dashboards [slice here] /xxx/xxx/xxx
+ router.push(split.slice(0, 4).join('/'));
} else {
router.push(`/${organizationId}/${value}`);
}
diff --git a/apps/web/src/app/(app)/[organizationId]/[projectId]/overview-metrics.tsx b/apps/web/src/app/(app)/[organizationId]/[projectId]/overview-metrics.tsx
index bf1fb31c..e1a32842 100644
--- a/apps/web/src/app/(app)/[organizationId]/[projectId]/overview-metrics.tsx
+++ b/apps/web/src/app/(app)/[organizationId]/[projectId]/overview-metrics.tsx
@@ -17,7 +17,7 @@ export default function OverviewMetrics({ projectId }: OverviewMetricsProps) {
const { previous, range, interval, metric, setMetric, startDate, endDate } =
useOverviewOptions();
const [filters] = useEventQueryFilters();
-
+ const isPageFilter = filters.find((filter) => filter.name === 'path');
const reports = [
{
id: 'Visitors',
@@ -29,7 +29,7 @@ export default function OverviewMetrics({ projectId }: OverviewMetricsProps) {
segment: 'user',
filters,
id: 'A',
- name: 'session_start',
+ name: isPageFilter ? 'screen_view' : 'session_start',
displayName: 'Visitors',
},
],
@@ -49,10 +49,10 @@ export default function OverviewMetrics({ projectId }: OverviewMetricsProps) {
endDate,
events: [
{
- segment: 'event',
+ segment: 'session',
filters,
id: 'A',
- name: 'session_start',
+ name: isPageFilter ? 'screen_view' : 'session_start',
displayName: 'Sessions',
},
],
@@ -122,7 +122,7 @@ export default function OverviewMetrics({ projectId }: OverviewMetricsProps) {
filters: [
{
id: '1',
- name: 'properties._bounce',
+ name: 'properties.__bounce',
operator: 'is',
value: ['true'],
},
@@ -171,8 +171,8 @@ export default function OverviewMetrics({ projectId }: OverviewMetricsProps) {
],
id: 'A',
property: 'duration',
- name: 'screen_view',
- displayName: 'Visit duration',
+ name: isPageFilter ? 'screen_view' : 'session_end',
+ displayName: isPageFilter ? 'Time on page' : 'Visit duration',
},
],
breakdowns: [],
diff --git a/apps/web/src/components/overview/overview-top-devices.tsx b/apps/web/src/components/overview/overview-top-devices.tsx
index 0b3daebb..ca2085ce 100644
--- a/apps/web/src/components/overview/overview-top-devices.tsx
+++ b/apps/web/src/components/overview/overview-top-devices.tsx
@@ -31,7 +31,7 @@ export default function OverviewTopDevices({
segment: 'user',
filters,
id: 'A',
- name: 'session_start',
+ name: '*',
},
],
breakdowns: [
@@ -61,7 +61,7 @@ export default function OverviewTopDevices({
segment: 'user',
filters,
id: 'A',
- name: 'session_start',
+ name: '*',
},
],
breakdowns: [
@@ -91,7 +91,7 @@ export default function OverviewTopDevices({
segment: 'user',
filters,
id: 'A',
- name: 'session_start',
+ name: '*',
},
],
breakdowns: [
@@ -121,7 +121,7 @@ export default function OverviewTopDevices({
segment: 'user',
filters,
id: 'A',
- name: 'session_start',
+ name: '*',
},
],
breakdowns: [
@@ -151,7 +151,7 @@ export default function OverviewTopDevices({
segment: 'user',
filters,
id: 'A',
- name: 'session_start',
+ name: '*',
},
],
breakdowns: [
@@ -189,7 +189,30 @@ export default function OverviewTopDevices({
-
+ {
+ switch (widget.key) {
+ case 'devices':
+ setFilter('device', item.name);
+ break;
+ case 'browser':
+ setFilter('browser', item.name);
+ break;
+ case 'browser_version':
+ setFilter('browser_version', item.name);
+ break;
+ case 'os':
+ setFilter('os', item.name);
+ break;
+ case 'os_version':
+ setFilter('os_version', item.name);
+ break;
+ }
+ }}
+ />
>
diff --git a/apps/web/src/components/overview/overview-top-geo.tsx b/apps/web/src/components/overview/overview-top-geo.tsx
index 7e0e3b09..9a1e3146 100644
--- a/apps/web/src/components/overview/overview-top-geo.tsx
+++ b/apps/web/src/components/overview/overview-top-geo.tsx
@@ -29,7 +29,7 @@ export default function OverviewTopGeo({ projectId }: OverviewTopGeoProps) {
segment: 'event',
filters,
id: 'A',
- name: 'session_start',
+ name: '*',
},
],
breakdowns: [
@@ -59,7 +59,7 @@ export default function OverviewTopGeo({ projectId }: OverviewTopGeoProps) {
segment: 'event',
filters,
id: 'A',
- name: 'session_start',
+ name: '*',
},
],
breakdowns: [
@@ -89,7 +89,7 @@ export default function OverviewTopGeo({ projectId }: OverviewTopGeoProps) {
segment: 'event',
filters,
id: 'A',
- name: 'session_start',
+ name: '*',
},
],
breakdowns: [
@@ -165,7 +165,7 @@ export default function OverviewTopGeo({ projectId }: OverviewTopGeoProps) {
segment: 'event',
filters,
id: 'A',
- name: 'session_start',
+ name: '*',
},
],
breakdowns: [
diff --git a/apps/web/src/components/overview/overview-top-sources.tsx b/apps/web/src/components/overview/overview-top-sources.tsx
index b139c25e..5a8e3329 100644
--- a/apps/web/src/components/overview/overview-top-sources.tsx
+++ b/apps/web/src/components/overview/overview-top-sources.tsx
@@ -18,6 +18,7 @@ export default function OverviewTopSources({
const { interval, range, previous, startDate, endDate } =
useOverviewOptions();
const [filters, setFilter] = useEventQueryFilters();
+ const isPageFilter = filters.find((filter) => filter.name === 'path');
const [widget, setWidget, widgets] = useOverviewWidget('sources', {
all: {
title: 'Top sources',
@@ -31,7 +32,7 @@ export default function OverviewTopSources({
segment: 'event',
filters: filters,
id: 'A',
- name: 'session_start',
+ name: isPageFilter ? 'screen_view' : 'session_start',
},
],
breakdowns: [
@@ -61,7 +62,7 @@ export default function OverviewTopSources({
segment: 'event',
filters: filters,
id: 'A',
- name: 'session_start',
+ name: isPageFilter ? 'screen_view' : 'session_start',
},
],
breakdowns: [
@@ -91,7 +92,7 @@ export default function OverviewTopSources({
segment: 'event',
filters: filters,
id: 'A',
- name: 'session_start',
+ name: isPageFilter ? 'screen_view' : 'session_start',
},
],
breakdowns: [
@@ -121,7 +122,7 @@ export default function OverviewTopSources({
segment: 'event',
filters,
id: 'A',
- name: 'session_start',
+ name: isPageFilter ? 'screen_view' : 'session_start',
},
],
breakdowns: [
@@ -151,7 +152,7 @@ export default function OverviewTopSources({
segment: 'event',
filters,
id: 'A',
- name: 'session_start',
+ name: isPageFilter ? 'screen_view' : 'session_start',
},
],
breakdowns: [
@@ -181,7 +182,7 @@ export default function OverviewTopSources({
segment: 'event',
filters,
id: 'A',
- name: 'session_start',
+ name: isPageFilter ? 'screen_view' : 'session_start',
},
],
breakdowns: [
@@ -211,7 +212,7 @@ export default function OverviewTopSources({
segment: 'event',
filters,
id: 'A',
- name: 'session_start',
+ name: isPageFilter ? 'screen_view' : 'session_start',
},
],
breakdowns: [
@@ -241,7 +242,7 @@ export default function OverviewTopSources({
segment: 'event',
filters,
id: 'A',
- name: 'session_start',
+ name: isPageFilter ? 'screen_view' : 'session_start',
},
],
breakdowns: [
diff --git a/apps/web/src/components/report/chart/MetricCard.tsx b/apps/web/src/components/report/chart/MetricCard.tsx
index cbb12418..5d8fb760 100644
--- a/apps/web/src/components/report/chart/MetricCard.tsx
+++ b/apps/web/src/components/report/chart/MetricCard.tsx
@@ -29,7 +29,6 @@ export function MetricCard({
unit,
}: MetricCardProps) {
const { previousIndicatorInverted } = useChartContext();
- const color = _color || theme?.colors['chart-0'];
const number = useNumber();
const renderValue = (value: number, unitClassName?: string) => {
diff --git a/apps/web/src/components/report/sidebar/ReportEvents.tsx b/apps/web/src/components/report/sidebar/ReportEvents.tsx
index 80c19262..1b21aa82 100644
--- a/apps/web/src/components/report/sidebar/ReportEvents.tsx
+++ b/apps/web/src/components/report/sidebar/ReportEvents.tsx
@@ -112,6 +112,10 @@ export function ReportEvents() {
value: 'user',
label: 'Unique users',
},
+ {
+ value: 'session',
+ label: 'Unique sessions',
+ },
{
value: 'user_average',
label: 'Average event per user',
@@ -136,6 +140,10 @@ export function ReportEvents() {
<>
Unique users
>
+ ) : event.segment === 'session' ? (
+ <>
+ Unique sessions
+ >
) : event.segment === 'user_average' ? (
<>
Average event per user
diff --git a/apps/web/src/utils/math.ts b/apps/web/src/utils/math.ts
index e7a77151..c24eb461 100644
--- a/apps/web/src/utils/math.ts
+++ b/apps/web/src/utils/math.ts
@@ -7,7 +7,8 @@ export const round = (num: number, decimals = 2) => {
export const average = (arr: (number | null)[]) => {
const filtered = arr.filter(
- (n): n is number => isNumber(n) && !Number.isNaN(n) && Number.isFinite(n)
+ (n): n is number =>
+ isNumber(n) && !Number.isNaN(n) && Number.isFinite(n) && n !== 0
);
const avg = filtered.reduce((p, c) => p + c, 0) / filtered.length;
return Number.isNaN(avg) ? 0 : avg;
diff --git a/packages/db/src/services/chart.service.ts b/packages/db/src/services/chart.service.ts
index 9226c350..56290e44 100644
--- a/packages/db/src/services/chart.service.ts
+++ b/packages/db/src/services/chart.service.ts
@@ -76,6 +76,10 @@ export function getChartSql({
sb.select.count = `countDistinct(profile_id) as count`;
}
+ if (event.segment === 'session') {
+ sb.select.count = `countDistinct(session_id) as count`;
+ }
+
if (event.segment === 'user_average') {
sb.select.count = `COUNT(*)::float / COUNT(DISTINCT profile_id)::float as count`;
}
diff --git a/packages/db/src/services/dashboard.service.ts b/packages/db/src/services/dashboard.service.ts
index 126bfef6..52bae331 100644
--- a/packages/db/src/services/dashboard.service.ts
+++ b/packages/db/src/services/dashboard.service.ts
@@ -5,10 +5,11 @@ export type IServiceDashboards = Awaited<
ReturnType
>;
-export async function getDashboardById(id: string) {
+export async function getDashboardById(id: string, projectId: string) {
const dashboard = await db.dashboard.findUnique({
where: {
id,
+ project_id: projectId,
},
include: {
project: true,
diff --git a/packages/validation/src/index.ts b/packages/validation/src/index.ts
index 550df515..9084ebd0 100644
--- a/packages/validation/src/index.ts
+++ b/packages/validation/src/index.ts
@@ -26,6 +26,7 @@ export const zChartEvent = z.object({
segment: z.enum([
'event',
'user',
+ 'session',
'user_average',
'one_event_per_user',
'property_sum',