Skip to content

Commit 1049931

Browse files
committed
feat: less processing, better stuff
1 parent 8e49cf8 commit 1049931

File tree

5 files changed

+49
-184
lines changed

5 files changed

+49
-184
lines changed

apps/api/src/query/builders/errors.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,18 @@ export const ErrorsBuilders: Record<string, SimpleQueryConfig> = {
7979
timeField: 'timestamp',
8080
allowedFilters: ['message', 'path', 'browser_name', 'country'],
8181
},
82+
83+
error_summary: {
84+
table: Analytics.errors,
85+
fields: [
86+
'COUNT(*) as totalErrors',
87+
'uniq(message) as uniqueErrorTypes',
88+
'uniq(anonymous_id) as affectedUsers',
89+
'uniq(session_id) as affectedSessions',
90+
],
91+
where: ["message != ''"],
92+
timeField: 'timestamp',
93+
allowedFilters: ['message', 'path', 'browser_name', 'country'],
94+
customizable: true,
95+
},
8296
};

apps/api/src/query/builders/pages.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ export const PagesBuilders: Record<string, SimpleQueryConfig> = {
66
top_pages: {
77
table: Analytics.events,
88
fields: [
9-
"CASE WHEN trimRight(path(path), '/') = '' THEN '/' ELSE trimRight(path(path), '/') END as name",
9+
"decodeURLComponent(CASE WHEN trimRight(path(path), '/') = '' THEN '/' ELSE trimRight(path(path), '/') END) as name",
1010
'COUNT(*) as pageviews',
1111
'COUNT(DISTINCT anonymous_id) as visitors',
1212
'ROUND((COUNT(*) / SUM(COUNT(*)) OVER()) * 100, 2) as percentage',
1313
],
1414
where: ["event_name = 'screen_view'"],
1515
groupBy: [
16-
"CASE WHEN trimRight(path(path), '/') = '' THEN '/' ELSE trimRight(path(path), '/') END",
16+
"decodeURLComponent(CASE WHEN trimRight(path(path), '/') = '' THEN '/' ELSE trimRight(path(path), '/') END)",
1717
],
1818
orderBy: 'pageviews DESC',
1919
limit: 100,
@@ -199,7 +199,7 @@ export const PagesBuilders: Record<string, SimpleQueryConfig> = {
199199
],
200200
where: ["event_name = 'screen_view'"],
201201
groupBy: [
202-
"CASE WHEN trimRight(path(path), '/') = '' THEN '/' ELSE trimRight(path(path), '/') END",
202+
"decodeURLComponent(CASE WHEN trimRight(path(path), '/') = '' THEN '/' ELSE trimRight(path(path), '/') END)",
203203
],
204204
orderBy: 'pageviews DESC',
205205
limit: 100,

apps/dashboard/app/(main)/websites/[id]/_components/tabs/audience-tab.tsx

Lines changed: 26 additions & 166 deletions
Original file line numberDiff line numberDiff line change
@@ -64,17 +64,6 @@ interface BrowserEntry {
6464
versions: BrowserVersion[];
6565
}
6666

67-
interface RawBrowserData {
68-
name?: string;
69-
browser_name?: string;
70-
browser_version?: string;
71-
visitors?: number;
72-
pageviews?: number;
73-
sessions?: number;
74-
percentage?: number;
75-
versions?: BrowserVersion[];
76-
}
77-
7867
interface ScreenResolutionEntry {
7968
name: string;
8069
visitors: number;
@@ -98,7 +87,6 @@ interface ProcessedData {
9887
languages: GeographicEntry[];
9988
};
10089
device: DeviceData;
101-
browsers: RawBrowserData[];
10290
}
10391

10492
const formatNumber = (value: number | null | undefined): string => {
@@ -143,15 +131,7 @@ const getConnectionIcon = (connection: string): React.ReactNode => {
143131
return <GlobeIcon className="h-4 w-4 text-primary" />;
144132
};
145133

146-
const normalizeData = (data: any[]): GeographicEntry[] =>
147-
data?.map((item: any) => ({
148-
name: item.country_name || item.name || 'Unknown',
149-
visitors: item.visitors || 0,
150-
pageviews: item.pageviews || 0,
151-
percentage: item.percentage || 0,
152-
country_code: item.country_code,
153-
country_name: item.country_name,
154-
})) || [];
134+
// API already provides clean, normalized data - no transformation needed
155135

156136
export function WebsiteAudienceTab({
157137
websiteId,
@@ -181,12 +161,6 @@ export function WebsiteAudienceTab({
181161
limit: 50,
182162
filters,
183163
},
184-
{
185-
id: 'browsers-grouped',
186-
parameters: ['browsers_grouped'],
187-
limit: 50,
188-
filters,
189-
},
190164
],
191165
[filters]
192166
);
@@ -245,25 +219,22 @@ export function WebsiteAudienceTab({
245219
screen_resolution: [],
246220
connection_type: [],
247221
},
248-
browsers: [],
249222
};
250223
}
251224

252225
const geographicResult = batchResults.find(
253226
(r) => r.queryId === 'geographic-data'
254227
);
255228
const deviceResult = batchResults.find((r) => r.queryId === 'device-data');
256-
const browsersResult = batchResults.find(
257-
(r) => r.queryId === 'browsers-grouped'
258-
);
229+
259230

260231
return {
261232
geographic: {
262-
countries: normalizeData(geographicResult?.data?.country || []),
263-
regions: normalizeData(geographicResult?.data?.region || []),
264-
cities: normalizeData(geographicResult?.data?.city || []),
265-
timezones: normalizeData(geographicResult?.data?.timezone || []),
266-
languages: normalizeData(geographicResult?.data?.language || []),
233+
countries: geographicResult?.data?.country || [],
234+
regions: geographicResult?.data?.region || [],
235+
cities: geographicResult?.data?.city || [],
236+
timezones: geographicResult?.data?.timezone || [],
237+
languages: geographicResult?.data?.language || [],
267238
},
268239
device: {
269240
device_type: deviceResult?.data?.device_type || [],
@@ -272,122 +243,26 @@ export function WebsiteAudienceTab({
272243
screen_resolution: deviceResult?.data?.screen_resolution || [],
273244
connection_type: deviceResult?.data?.connection_type || [],
274245
},
275-
browsers: browsersResult?.data?.browsers_grouped || [],
276246
};
277247
}, [batchResults]);
278248

279249
const processedBrowserData = useMemo((): BrowserEntry[] => {
280-
const rawData = processedData.browsers;
281-
if (!rawData?.length) {
282-
return [];
283-
}
284-
285-
if (rawData.length > 0 && rawData[0].versions) {
286-
return rawData
287-
.map(
288-
(browser: RawBrowserData) =>
289-
({
290-
...browser,
291-
name: browser.name || 'Unknown',
292-
browserName: browser.name || 'Unknown',
293-
visitors: browser.visitors || 0,
294-
pageviews: browser.pageviews || 0,
295-
sessions: browser.sessions || 0,
296-
percentage: browser.percentage || 0,
297-
versions:
298-
browser.versions?.sort(
299-
(a: BrowserVersion, b: BrowserVersion) =>
300-
(b.visitors || 0) - (a.visitors || 0)
301-
) || [],
302-
}) as BrowserEntry
303-
)
304-
.sort(
305-
(a: BrowserEntry, b: BrowserEntry) =>
306-
(b.visitors || 0) - (a.visitors || 0)
307-
);
308-
}
309-
310-
const browserGroups: Record<string, BrowserEntry> = rawData.reduce(
311-
(acc: Record<string, BrowserEntry>, browser: RawBrowserData) => {
312-
const browserName = browser.browser_name || 'Unknown';
313-
if (!acc[browserName]) {
314-
acc[browserName] = {
315-
name: browserName,
316-
browserName,
317-
visitors: 0,
318-
pageviews: 0,
319-
sessions: 0,
320-
percentage: 0,
321-
versions: [],
322-
};
323-
}
324-
325-
acc[browserName].visitors += browser.visitors || 0;
326-
acc[browserName].pageviews += browser.pageviews || 0;
327-
acc[browserName].sessions += browser.sessions || 0;
328-
acc[browserName].versions.push({
329-
version: browser.browser_version || 'Unknown',
330-
visitors: browser.visitors || 0,
331-
pageviews: browser.pageviews || 0,
332-
sessions: browser.sessions || 0,
333-
});
334-
335-
return acc;
336-
},
337-
{}
338-
);
339-
340-
const browserArray = Object.values(browserGroups) as BrowserEntry[];
341-
const totalVisitors = browserArray.reduce(
342-
(sum: number, browser: BrowserEntry) => sum + (browser.visitors || 0),
343-
0
344-
);
345-
346-
return browserArray
347-
.map((browser: BrowserEntry) => ({
348-
...browser,
349-
percentage:
350-
totalVisitors > 0
351-
? Math.round((browser.visitors / totalVisitors) * 100)
352-
: 0,
353-
versions:
354-
browser.versions?.sort(
355-
(a: BrowserVersion, b: BrowserVersion) =>
356-
(b.visitors || 0) - (a.visitors || 0)
357-
) || [],
358-
}))
359-
.sort(
360-
(a: BrowserEntry, b: BrowserEntry) =>
361-
(b.visitors || 0) - (a.visitors || 0)
362-
);
363-
}, [processedData.browsers]);
250+
// API already provides clean data with percentages and sorting
251+
return (processedData.device.browser_name || []).map((browser: any) => ({
252+
...browser,
253+
browserName: browser.name,
254+
sessions: browser.sessions || 0,
255+
versions: [], // No versions in simple browser data
256+
}));
257+
}, [processedData.device.browser_name]);
364258

365259
const processedConnectionData = useMemo((): ConnectionEntry[] => {
366-
const connectionData = processedData.device.connection_type;
367-
if (!connectionData?.length) {
368-
return [];
369-
}
370-
371-
const totalVisitors = connectionData.reduce(
372-
(sum: number, item: any) => sum + item.visitors,
373-
0
374-
);
375-
376-
return connectionData
377-
.map((item: any) => ({
378-
name: item.name || 'Unknown',
379-
visitors: item.visitors,
380-
pageviews: item.pageviews || 0,
381-
percentage:
382-
totalVisitors > 0
383-
? Math.round((item.visitors / totalVisitors) * 100)
384-
: 0,
385-
iconComponent: getConnectionIcon(item.name || ''),
386-
category: 'connection' as const,
387-
}))
388-
.sort(
389-
(a: ConnectionEntry, b: ConnectionEntry) => b.visitors - a.visitors
390-
);
260+
// API already provides sorted data with percentages
261+
return (processedData.device.connection_type || []).map((item: any) => ({
262+
...item,
263+
iconComponent: getConnectionIcon(item.name || ''),
264+
category: 'connection' as const,
265+
}));
391266
}, [processedData.device.connection_type]);
392267

393268
const isLoading = isBatchLoading || isRefreshing;
@@ -796,10 +671,7 @@ export function WebsiteAudienceTab({
796671
{
797672
id: 'countries',
798673
label: 'Countries',
799-
data: processedData.geographic.countries.map((item, index) => ({
800-
...item,
801-
_uniqueKey: `country-${item.country_code || item.name}-${index}`,
802-
})),
674+
data: processedData.geographic.countries,
803675
columns: countryColumns,
804676
getFilter: (row: any) => ({
805677
field: 'country',
@@ -809,10 +681,7 @@ export function WebsiteAudienceTab({
809681
{
810682
id: 'regions',
811683
label: 'Regions',
812-
data: processedData.geographic.regions.map((item, index) => ({
813-
...item,
814-
_uniqueKey: `region-${item.name}-${index}`,
815-
})),
684+
data: processedData.geographic.regions,
816685
columns: geographicColumns,
817686
getFilter: (row: any) => ({
818687
field: 'region',
@@ -822,10 +691,7 @@ export function WebsiteAudienceTab({
822691
{
823692
id: 'cities',
824693
label: 'Cities',
825-
data: processedData.geographic.cities.map((item, index) => ({
826-
...item,
827-
_uniqueKey: `city-${item.name}-${index}`,
828-
})),
694+
data: processedData.geographic.cities,
829695
columns: cityColumns,
830696
getFilter: (row: any) => ({
831697
field: 'city',
@@ -837,10 +703,7 @@ export function WebsiteAudienceTab({
837703
{
838704
id: 'languages',
839705
label: 'Languages',
840-
data: processedData.geographic.languages.map((item, index) => ({
841-
...item,
842-
_uniqueKey: `language-${item.name}-${index}`,
843-
})),
706+
data: processedData.geographic.languages,
844707
columns: languageColumns,
845708
getFilter: (row: any) => ({
846709
field: 'language',
@@ -852,10 +715,7 @@ export function WebsiteAudienceTab({
852715
{
853716
id: 'timezones',
854717
label: 'Timezones',
855-
data: processedData.geographic.timezones.map((item, index) => ({
856-
...item,
857-
_uniqueKey: `timezone-${item.name}-${index}`,
858-
})),
718+
data: processedData.geographic.timezones,
859719
columns: timezoneColumns,
860720
getFilter: (row: any) => ({
861721
field: 'timezone',

apps/dashboard/app/(main)/websites/[id]/_components/tabs/overview-tab.tsx

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -357,10 +357,7 @@ export function WebsiteOverviewTab({
357357

358358
const pagesTabs = useTableTabs({
359359
top_pages: {
360-
data: (analytics.top_pages || []).map((page: PageData) => ({
361-
...page,
362-
name: decodeURIComponent(page.name),
363-
})),
360+
data: analytics.top_pages || [],
364361
label: 'Top Pages',
365362
primaryField: 'name',
366363
primaryHeader: 'Page',
@@ -370,10 +367,7 @@ export function WebsiteOverviewTab({
370367
}),
371368
},
372369
entry_pages: {
373-
data: (analytics.entry_pages || []).map((page: PageData) => ({
374-
...page,
375-
name: decodeURIComponent(page.name),
376-
})),
370+
data: analytics.entry_pages || [],
377371
label: 'Entry Pages',
378372
primaryField: 'name',
379373
primaryHeader: 'Page',
@@ -383,10 +377,7 @@ export function WebsiteOverviewTab({
383377
}),
384378
},
385379
exit_pages: {
386-
data: (analytics.exit_pages || []).map((page: PageData) => ({
387-
...page,
388-
name: decodeURIComponent(page.name),
389-
})),
380+
data: analytics.exit_pages || [],
390381
label: 'Exit Pages',
391382
primaryField: 'name',
392383
primaryHeader: 'Page',

apps/dashboard/components/charts/metrics-chart.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { LineChart } from 'lucide-react';
1+
import { ChartLineIcon } from '@phosphor-icons/react';
22
import { useCallback, useMemo, useState } from 'react';
33
import {
44
Area,
@@ -142,7 +142,7 @@ export function MetricsChart({
142142
>
143143
<CardHeader className="px-6 py-6">
144144
<CardTitle className="flex items-center gap-2 font-semibold text-lg">
145-
<LineChart className="h-5 w-5 text-primary" />
145+
<ChartLineIcon className="h-5 w-5 text-primary" />
146146
{title}
147147
</CardTitle>
148148
{description && (
@@ -152,7 +152,7 @@ export function MetricsChart({
152152
<CardContent className="flex items-center justify-center p-8">
153153
<div className="py-12 text-center">
154154
<div className="relative">
155-
<LineChart
155+
<ChartLineIcon
156156
className="mx-auto h-16 w-16 text-muted-foreground/20"
157157
strokeWidth={1.5}
158158
/>

0 commit comments

Comments
 (0)