fixed alot of bugs in overview
This commit is contained in:
@@ -18,9 +18,9 @@ export default async function Page({
|
|||||||
params: { organizationId, projectId, dashboardId },
|
params: { organizationId, projectId, dashboardId },
|
||||||
}: PageProps) {
|
}: PageProps) {
|
||||||
const [dashboard, reports] = await Promise.all([
|
const [dashboard, reports] = await Promise.all([
|
||||||
getDashboardById(dashboardId),
|
getDashboardById(dashboardId, projectId),
|
||||||
getReportsByDashboardId(dashboardId),
|
getReportsByDashboardId(dashboardId),
|
||||||
getExists(organizationId, projectId),
|
getExists(organizationId),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (!dashboard) {
|
if (!dashboard) {
|
||||||
|
|||||||
@@ -23,12 +23,10 @@ export default function LayoutProjectSelector({
|
|||||||
className="w-auto min-w-0 max-sm:max-w-[100px]"
|
className="w-auto min-w-0 max-sm:max-w-[100px]"
|
||||||
placeholder={'Select project'}
|
placeholder={'Select project'}
|
||||||
onChange={(value) => {
|
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) {
|
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 {
|
} else {
|
||||||
router.push(`/${organizationId}/${value}`);
|
router.push(`/${organizationId}/${value}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export default function OverviewMetrics({ projectId }: OverviewMetricsProps) {
|
|||||||
const { previous, range, interval, metric, setMetric, startDate, endDate } =
|
const { previous, range, interval, metric, setMetric, startDate, endDate } =
|
||||||
useOverviewOptions();
|
useOverviewOptions();
|
||||||
const [filters] = useEventQueryFilters();
|
const [filters] = useEventQueryFilters();
|
||||||
|
const isPageFilter = filters.find((filter) => filter.name === 'path');
|
||||||
const reports = [
|
const reports = [
|
||||||
{
|
{
|
||||||
id: 'Visitors',
|
id: 'Visitors',
|
||||||
@@ -29,7 +29,7 @@ export default function OverviewMetrics({ projectId }: OverviewMetricsProps) {
|
|||||||
segment: 'user',
|
segment: 'user',
|
||||||
filters,
|
filters,
|
||||||
id: 'A',
|
id: 'A',
|
||||||
name: 'session_start',
|
name: isPageFilter ? 'screen_view' : 'session_start',
|
||||||
displayName: 'Visitors',
|
displayName: 'Visitors',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -49,10 +49,10 @@ export default function OverviewMetrics({ projectId }: OverviewMetricsProps) {
|
|||||||
endDate,
|
endDate,
|
||||||
events: [
|
events: [
|
||||||
{
|
{
|
||||||
segment: 'event',
|
segment: 'session',
|
||||||
filters,
|
filters,
|
||||||
id: 'A',
|
id: 'A',
|
||||||
name: 'session_start',
|
name: isPageFilter ? 'screen_view' : 'session_start',
|
||||||
displayName: 'Sessions',
|
displayName: 'Sessions',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -122,7 +122,7 @@ export default function OverviewMetrics({ projectId }: OverviewMetricsProps) {
|
|||||||
filters: [
|
filters: [
|
||||||
{
|
{
|
||||||
id: '1',
|
id: '1',
|
||||||
name: 'properties._bounce',
|
name: 'properties.__bounce',
|
||||||
operator: 'is',
|
operator: 'is',
|
||||||
value: ['true'],
|
value: ['true'],
|
||||||
},
|
},
|
||||||
@@ -171,8 +171,8 @@ export default function OverviewMetrics({ projectId }: OverviewMetricsProps) {
|
|||||||
],
|
],
|
||||||
id: 'A',
|
id: 'A',
|
||||||
property: 'duration',
|
property: 'duration',
|
||||||
name: 'screen_view',
|
name: isPageFilter ? 'screen_view' : 'session_end',
|
||||||
displayName: 'Visit duration',
|
displayName: isPageFilter ? 'Time on page' : 'Visit duration',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
breakdowns: [],
|
breakdowns: [],
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export default function OverviewTopDevices({
|
|||||||
segment: 'user',
|
segment: 'user',
|
||||||
filters,
|
filters,
|
||||||
id: 'A',
|
id: 'A',
|
||||||
name: 'session_start',
|
name: '*',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
breakdowns: [
|
breakdowns: [
|
||||||
@@ -61,7 +61,7 @@ export default function OverviewTopDevices({
|
|||||||
segment: 'user',
|
segment: 'user',
|
||||||
filters,
|
filters,
|
||||||
id: 'A',
|
id: 'A',
|
||||||
name: 'session_start',
|
name: '*',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
breakdowns: [
|
breakdowns: [
|
||||||
@@ -91,7 +91,7 @@ export default function OverviewTopDevices({
|
|||||||
segment: 'user',
|
segment: 'user',
|
||||||
filters,
|
filters,
|
||||||
id: 'A',
|
id: 'A',
|
||||||
name: 'session_start',
|
name: '*',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
breakdowns: [
|
breakdowns: [
|
||||||
@@ -121,7 +121,7 @@ export default function OverviewTopDevices({
|
|||||||
segment: 'user',
|
segment: 'user',
|
||||||
filters,
|
filters,
|
||||||
id: 'A',
|
id: 'A',
|
||||||
name: 'session_start',
|
name: '*',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
breakdowns: [
|
breakdowns: [
|
||||||
@@ -151,7 +151,7 @@ export default function OverviewTopDevices({
|
|||||||
segment: 'user',
|
segment: 'user',
|
||||||
filters,
|
filters,
|
||||||
id: 'A',
|
id: 'A',
|
||||||
name: 'session_start',
|
name: '*',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
breakdowns: [
|
breakdowns: [
|
||||||
@@ -189,7 +189,30 @@ export default function OverviewTopDevices({
|
|||||||
</WidgetButtons>
|
</WidgetButtons>
|
||||||
</WidgetHead>
|
</WidgetHead>
|
||||||
<WidgetBody>
|
<WidgetBody>
|
||||||
<ChartSwitch hideID {...widget.chart} previous={false} />
|
<ChartSwitch
|
||||||
|
hideID
|
||||||
|
{...widget.chart}
|
||||||
|
previous={false}
|
||||||
|
onClick={(item) => {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</WidgetBody>
|
</WidgetBody>
|
||||||
</Widget>
|
</Widget>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export default function OverviewTopGeo({ projectId }: OverviewTopGeoProps) {
|
|||||||
segment: 'event',
|
segment: 'event',
|
||||||
filters,
|
filters,
|
||||||
id: 'A',
|
id: 'A',
|
||||||
name: 'session_start',
|
name: '*',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
breakdowns: [
|
breakdowns: [
|
||||||
@@ -59,7 +59,7 @@ export default function OverviewTopGeo({ projectId }: OverviewTopGeoProps) {
|
|||||||
segment: 'event',
|
segment: 'event',
|
||||||
filters,
|
filters,
|
||||||
id: 'A',
|
id: 'A',
|
||||||
name: 'session_start',
|
name: '*',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
breakdowns: [
|
breakdowns: [
|
||||||
@@ -89,7 +89,7 @@ export default function OverviewTopGeo({ projectId }: OverviewTopGeoProps) {
|
|||||||
segment: 'event',
|
segment: 'event',
|
||||||
filters,
|
filters,
|
||||||
id: 'A',
|
id: 'A',
|
||||||
name: 'session_start',
|
name: '*',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
breakdowns: [
|
breakdowns: [
|
||||||
@@ -165,7 +165,7 @@ export default function OverviewTopGeo({ projectId }: OverviewTopGeoProps) {
|
|||||||
segment: 'event',
|
segment: 'event',
|
||||||
filters,
|
filters,
|
||||||
id: 'A',
|
id: 'A',
|
||||||
name: 'session_start',
|
name: '*',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
breakdowns: [
|
breakdowns: [
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ export default function OverviewTopSources({
|
|||||||
const { interval, range, previous, startDate, endDate } =
|
const { interval, range, previous, startDate, endDate } =
|
||||||
useOverviewOptions();
|
useOverviewOptions();
|
||||||
const [filters, setFilter] = useEventQueryFilters();
|
const [filters, setFilter] = useEventQueryFilters();
|
||||||
|
const isPageFilter = filters.find((filter) => filter.name === 'path');
|
||||||
const [widget, setWidget, widgets] = useOverviewWidget('sources', {
|
const [widget, setWidget, widgets] = useOverviewWidget('sources', {
|
||||||
all: {
|
all: {
|
||||||
title: 'Top sources',
|
title: 'Top sources',
|
||||||
@@ -31,7 +32,7 @@ export default function OverviewTopSources({
|
|||||||
segment: 'event',
|
segment: 'event',
|
||||||
filters: filters,
|
filters: filters,
|
||||||
id: 'A',
|
id: 'A',
|
||||||
name: 'session_start',
|
name: isPageFilter ? 'screen_view' : 'session_start',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
breakdowns: [
|
breakdowns: [
|
||||||
@@ -61,7 +62,7 @@ export default function OverviewTopSources({
|
|||||||
segment: 'event',
|
segment: 'event',
|
||||||
filters: filters,
|
filters: filters,
|
||||||
id: 'A',
|
id: 'A',
|
||||||
name: 'session_start',
|
name: isPageFilter ? 'screen_view' : 'session_start',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
breakdowns: [
|
breakdowns: [
|
||||||
@@ -91,7 +92,7 @@ export default function OverviewTopSources({
|
|||||||
segment: 'event',
|
segment: 'event',
|
||||||
filters: filters,
|
filters: filters,
|
||||||
id: 'A',
|
id: 'A',
|
||||||
name: 'session_start',
|
name: isPageFilter ? 'screen_view' : 'session_start',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
breakdowns: [
|
breakdowns: [
|
||||||
@@ -121,7 +122,7 @@ export default function OverviewTopSources({
|
|||||||
segment: 'event',
|
segment: 'event',
|
||||||
filters,
|
filters,
|
||||||
id: 'A',
|
id: 'A',
|
||||||
name: 'session_start',
|
name: isPageFilter ? 'screen_view' : 'session_start',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
breakdowns: [
|
breakdowns: [
|
||||||
@@ -151,7 +152,7 @@ export default function OverviewTopSources({
|
|||||||
segment: 'event',
|
segment: 'event',
|
||||||
filters,
|
filters,
|
||||||
id: 'A',
|
id: 'A',
|
||||||
name: 'session_start',
|
name: isPageFilter ? 'screen_view' : 'session_start',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
breakdowns: [
|
breakdowns: [
|
||||||
@@ -181,7 +182,7 @@ export default function OverviewTopSources({
|
|||||||
segment: 'event',
|
segment: 'event',
|
||||||
filters,
|
filters,
|
||||||
id: 'A',
|
id: 'A',
|
||||||
name: 'session_start',
|
name: isPageFilter ? 'screen_view' : 'session_start',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
breakdowns: [
|
breakdowns: [
|
||||||
@@ -211,7 +212,7 @@ export default function OverviewTopSources({
|
|||||||
segment: 'event',
|
segment: 'event',
|
||||||
filters,
|
filters,
|
||||||
id: 'A',
|
id: 'A',
|
||||||
name: 'session_start',
|
name: isPageFilter ? 'screen_view' : 'session_start',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
breakdowns: [
|
breakdowns: [
|
||||||
@@ -241,7 +242,7 @@ export default function OverviewTopSources({
|
|||||||
segment: 'event',
|
segment: 'event',
|
||||||
filters,
|
filters,
|
||||||
id: 'A',
|
id: 'A',
|
||||||
name: 'session_start',
|
name: isPageFilter ? 'screen_view' : 'session_start',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
breakdowns: [
|
breakdowns: [
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ export function MetricCard({
|
|||||||
unit,
|
unit,
|
||||||
}: MetricCardProps) {
|
}: MetricCardProps) {
|
||||||
const { previousIndicatorInverted } = useChartContext();
|
const { previousIndicatorInverted } = useChartContext();
|
||||||
const color = _color || theme?.colors['chart-0'];
|
|
||||||
const number = useNumber();
|
const number = useNumber();
|
||||||
|
|
||||||
const renderValue = (value: number, unitClassName?: string) => {
|
const renderValue = (value: number, unitClassName?: string) => {
|
||||||
|
|||||||
@@ -112,6 +112,10 @@ export function ReportEvents() {
|
|||||||
value: 'user',
|
value: 'user',
|
||||||
label: 'Unique users',
|
label: 'Unique users',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
value: 'session',
|
||||||
|
label: 'Unique sessions',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
value: 'user_average',
|
value: 'user_average',
|
||||||
label: 'Average event per user',
|
label: 'Average event per user',
|
||||||
@@ -136,6 +140,10 @@ export function ReportEvents() {
|
|||||||
<>
|
<>
|
||||||
<Users size={12} /> Unique users
|
<Users size={12} /> Unique users
|
||||||
</>
|
</>
|
||||||
|
) : event.segment === 'session' ? (
|
||||||
|
<>
|
||||||
|
<Users size={12} /> Unique sessions
|
||||||
|
</>
|
||||||
) : event.segment === 'user_average' ? (
|
) : event.segment === 'user_average' ? (
|
||||||
<>
|
<>
|
||||||
<Users size={12} /> Average event per user
|
<Users size={12} /> Average event per user
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ export const round = (num: number, decimals = 2) => {
|
|||||||
|
|
||||||
export const average = (arr: (number | null)[]) => {
|
export const average = (arr: (number | null)[]) => {
|
||||||
const filtered = arr.filter(
|
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;
|
const avg = filtered.reduce((p, c) => p + c, 0) / filtered.length;
|
||||||
return Number.isNaN(avg) ? 0 : avg;
|
return Number.isNaN(avg) ? 0 : avg;
|
||||||
|
|||||||
@@ -76,6 +76,10 @@ export function getChartSql({
|
|||||||
sb.select.count = `countDistinct(profile_id) as count`;
|
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') {
|
if (event.segment === 'user_average') {
|
||||||
sb.select.count = `COUNT(*)::float / COUNT(DISTINCT profile_id)::float as count`;
|
sb.select.count = `COUNT(*)::float / COUNT(DISTINCT profile_id)::float as count`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,10 +5,11 @@ export type IServiceDashboards = Awaited<
|
|||||||
ReturnType<typeof getDashboardsByProjectId>
|
ReturnType<typeof getDashboardsByProjectId>
|
||||||
>;
|
>;
|
||||||
|
|
||||||
export async function getDashboardById(id: string) {
|
export async function getDashboardById(id: string, projectId: string) {
|
||||||
const dashboard = await db.dashboard.findUnique({
|
const dashboard = await db.dashboard.findUnique({
|
||||||
where: {
|
where: {
|
||||||
id,
|
id,
|
||||||
|
project_id: projectId,
|
||||||
},
|
},
|
||||||
include: {
|
include: {
|
||||||
project: true,
|
project: true,
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ export const zChartEvent = z.object({
|
|||||||
segment: z.enum([
|
segment: z.enum([
|
||||||
'event',
|
'event',
|
||||||
'user',
|
'user',
|
||||||
|
'session',
|
||||||
'user_average',
|
'user_average',
|
||||||
'one_event_per_user',
|
'one_event_per_user',
|
||||||
'property_sum',
|
'property_sum',
|
||||||
|
|||||||
Reference in New Issue
Block a user