Merge branch 'master' into custom-data-on-pageview

This commit is contained in:
Ewen Le Bihan 2023-12-17 03:02:42 +01:00
commit ef2d56fc78
7 changed files with 82 additions and 6 deletions

View file

@ -10,7 +10,13 @@ import WebsiteHeader from './WebsiteHeader';
import WebsiteMetricsBar from './WebsiteMetricsBar';
import WebsiteTableView from './WebsiteTableView';
export default function WebsiteDetails({ websiteId }: { websiteId: string }) {
export default function WebsiteDetails({
websiteId,
customDataFields,
}: {
websiteId: string;
customDataFields: string[];
}) {
const { data: website, isLoading, error } = useWebsite(websiteId);
const pathname = usePathname();
const showLinks = !pathname.includes('/share/');
@ -32,7 +38,13 @@ export default function WebsiteDetails({ websiteId }: { websiteId: string }) {
{!website && <Loading icon="dots" style={{ minHeight: 300 }} />}
{website && (
<>
{!view && <WebsiteTableView websiteId={websiteId} domainName={website.domain} />}
{!view && (
<WebsiteTableView
customDataFields={customDataFields}
websiteId={websiteId}
domainName={website.domain}
/>
)}
{view && <WebsiteExpandedView websiteId={websiteId} domainName={website.domain} />}
</>
)}

View file

@ -9,13 +9,17 @@ import WorldMap from 'components/metrics/WorldMap';
import CountriesTable from 'components/metrics/CountriesTable';
import EventsTable from 'components/metrics/EventsTable';
import EventsChart from 'components/metrics/EventsChart';
import PageviewCustomDataTable from 'components/metrics/PageviewCustomDataTable';
import { chunkArray } from 'next-basics';
export default function WebsiteTableView({
websiteId,
domainName,
customDataFields,
}: {
websiteId: string;
domainName: string;
customDataFields: string[];
}) {
const [countryData, setCountryData] = useState();
const tableProps = {
@ -35,6 +39,16 @@ export default function WebsiteTableView({
<OSTable {...tableProps} />
<DevicesTable {...tableProps} />
</GridRow>
{chunkArray(customDataFields, 3).map((fields, index) => (
<GridRow
key={index}
columns={fields.length === 1 ? 'one' : fields.length === 2 ? 'two' : 'three'}
>
{fields.map(field => (
<PageviewCustomDataTable key="custom" {...tableProps} fieldName={field} />
))}
</GridRow>
))}
<GridRow columns="two-one">
<WorldMap data={countryData} />
<CountriesTable {...tableProps} onDataLoad={setCountryData} />

View file

@ -0,0 +1,29 @@
import useMessages from 'components/hooks/useMessages';
import MetricsTable, { MetricsTableProps } from 'components/metrics/MetricsTable';
export function PageviewCustomDataTable(props: MetricsTableProps) {
const { formatMessage, labels } = useMessages();
function renderLink({ x: field }) {
return <>{field}</>;
}
function titleize(fieldName: string) {
return fieldName
.split(/[_-]/)
.map((word, i) => (i === 0 ? word[0].toUpperCase() + word.slice(1) : word))
.join(' ');
}
return (
<MetricsTable
{...props}
title={titleize(props.fieldName)}
type="custom"
metric={formatMessage(labels.visitors)}
renderLabel={renderLink}
/>
);
}
export default PageviewCustomDataTable;

View file

@ -28,6 +28,7 @@ export interface WebsiteMetricsRequestQuery {
limit?: number;
offset?: number;
search?: string;
fieldName?: string;
}
const schema = {
@ -51,6 +52,7 @@ const schema = {
limit: yup.number(),
offset: yup.number(),
search: yup.string(),
fieldName: yup.string(),
}),
};
@ -80,6 +82,7 @@ export default async (
limit,
offset,
search,
fieldName,
} = req.query;
if (req.method === 'GET') {
@ -146,6 +149,10 @@ export default async (
offset,
);
return ok(res, data);
} else if (type === 'custom') {
const data = await getPageviewMetrics(websiteId, column, filters, limit, fieldName);
return ok(res, data);
}

View file

@ -51,6 +51,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) {
on website_event.event_id = event_data.website_event_id
where event_data.website_id = {{websiteId::uuid}}
and event_data.created_at between {{startDate}} and {{endDate}}
and website_event.event_name is not null
group by website_event.event_name, event_data.event_key, event_data.data_type
order by 1 asc, 2 asc
limit 500

View file

@ -32,8 +32,10 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) {
event_key,
count(*) as "total"
from event_data
where website_id = {{websiteId::uuid}}
and created_at between {{startDate}} and {{endDate}}
join website_event on website_event.event_id = event_data.website_event_id
where website_event.website_id = {{websiteId::uuid}}
and event_data.created_at between {{startDate}} and {{endDate}}
and website_event.event_name is not null
${filterQuery}
group by website_event_id, event_key
) as t

View file

@ -11,6 +11,7 @@ export async function getPageviewMetrics(
filters: QueryFilters,
limit?: number,
offset?: number,
fieldName?: string,
]
) {
return runQuery({
@ -25,6 +26,7 @@ async function relationalQuery(
filters: QueryFilters,
limit: number = 500,
offset: number = 0,
fieldName?: string,
) {
const { rawQuery, parseFilters } = prisma;
const { filterQuery, joinSession, params } = await parseFilters(
@ -37,19 +39,26 @@ async function relationalQuery(
);
let excludeDomain = '';
let joinEventData = '';
let filterEventDataOnFieldName = '';
if (column === 'referrer_domain') {
excludeDomain =
'and (website_event.referrer_domain != {{websiteDomain}} or website_event.referrer_domain is null)';
} else if (column === 'custom') {
joinEventData = 'join event_data on event_data.website_event_id = website_event.event_id';
filterEventDataOnFieldName = 'and event_data.event_key = {{fieldName}}';
}
return rawQuery(
`
select ${column} x, count(*) y
select ${column === 'custom' ? `event_data.string_value` : column} x, count(*) y
from website_event
${joinEventData}
${joinSession}
where website_event.website_id = {{websiteId::uuid}}
and website_event.created_at between {{startDate}} and {{endDate}}
and event_type = {{eventType}}
${filterEventDataOnFieldName}
${excludeDomain}
${filterQuery}
group by 1
@ -57,7 +66,7 @@ async function relationalQuery(
limit ${limit}
offset ${offset}
`,
params,
{ ...params, fieldName },
);
}
@ -67,6 +76,8 @@ async function clickhouseQuery(
filters: QueryFilters,
limit: number = 500,
offset: number = 0,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
fieldName?: string,
): Promise<{ x: string; y: number }[]> {
const { rawQuery, parseFilters } = clickhouse;
const { filterQuery, params } = await parseFilters(websiteId, {