mirror of
https://github.com/umami-software/umami.git
synced 2026-02-06 21:57:16 +01:00
Merge branch 'dev' into jajaja
# Conflicts: # db/mysql/schema.prisma # package.json # pnpm-lock.yaml # src/app/(main)/reports/[reportId]/ReportBody.tsx # src/app/(main)/reports/[reportId]/ReportPage.tsx # src/app/(main)/reports/utm/UTMView.tsx # src/app/(main)/websites/[websiteId]/WebsiteMetricsBar.tsx # src/app/(main)/websites/[websiteId]/WebsiteTableView.tsx # src/app/(main)/websites/[websiteId]/events/EventsPage.tsx # src/app/api/reports/[reportId]/route.ts # src/app/api/websites/[websiteId]/metrics/route.ts # src/components/hooks/queries/useReport.ts # src/components/icons.ts # src/components/messages.ts # src/components/metrics/MetricsTable.module.css # src/components/metrics/MetricsTable.tsx # src/queries/sql/events/getEventMetrics.ts # src/queries/sql/reports/getUTM.ts
This commit is contained in:
commit
45c9ea9c22
28 changed files with 571 additions and 139 deletions
|
|
@ -1,28 +1,36 @@
|
|||
'use client';
|
||||
import { TabList, Tab, Tabs, TabPanel, Column } from '@umami/react-zen';
|
||||
import { EventsTable } from '@/components/metrics/EventsTable';
|
||||
import { useState } from 'react';
|
||||
import { useState, Key } from 'react';
|
||||
import { EventsDataTable } from './EventsDataTable';
|
||||
import { Panel } from '@/components/common/Panel';
|
||||
import { EventsChart } from '@/components/metrics/EventsChart';
|
||||
import { useMessages } from '@/components/hooks';
|
||||
import { EventProperties } from './EventProperties';
|
||||
import { WebsiteControls } from '@/app/(main)/websites/[websiteId]/WebsiteControls';
|
||||
import { getItem, setItem } from '@/lib/storage';
|
||||
|
||||
const KEY_NAME = 'umami.events.tab';
|
||||
|
||||
export function EventsPage({ websiteId }) {
|
||||
const [label, setLabel] = useState(null);
|
||||
const [tab, setTab] = useState('activity');
|
||||
const [tab, setTab] = useState(getItem(KEY_NAME) || 'activity');
|
||||
const { formatMessage, labels } = useMessages();
|
||||
|
||||
const handleLabelClick = (value: string) => {
|
||||
setLabel(value !== label ? value : '');
|
||||
};
|
||||
|
||||
const handleSelect = (value: Key) => {
|
||||
setItem(KEY_NAME, value);
|
||||
setTab(value);
|
||||
};
|
||||
|
||||
return (
|
||||
<Column gap="3">
|
||||
<WebsiteControls websiteId={websiteId} />
|
||||
<Panel>
|
||||
<Tabs selectedKey={tab} onSelectionChange={(value: any) => setTab(value)}>
|
||||
<Tabs selectedKey={tab} onSelectionChange={key => handleSelect(key)}>
|
||||
<TabList>
|
||||
<Tab id="activity">{formatMessage(labels.activity)}</Tab>
|
||||
<Tab id="chart">{formatMessage(labels.chart)}</Tab>
|
||||
|
|
|
|||
|
|
@ -51,8 +51,8 @@ export async function POST(
|
|||
type,
|
||||
name,
|
||||
description,
|
||||
parameters,
|
||||
});
|
||||
parameters: parameters,
|
||||
} as any);
|
||||
|
||||
return json(result);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { parseRequest, getQueryFilters } from '@/lib/request';
|
|||
import { unauthorized, json } from '@/lib/response';
|
||||
import { canViewWebsite } from '@/lib/auth';
|
||||
import { filterParams, timezoneParam, unitParam } from '@/lib/schema';
|
||||
import { getEventMetrics } from '@/queries';
|
||||
import { getEventStats } from '@/queries';
|
||||
|
||||
export async function GET(
|
||||
request: Request,
|
||||
|
|
@ -31,7 +31,7 @@ export async function GET(
|
|||
|
||||
const filters = await getQueryFilters(query, websiteId);
|
||||
|
||||
const data = await getEventMetrics(websiteId, filters);
|
||||
const data = await getEventStats(websiteId, filters);
|
||||
|
||||
return json(data);
|
||||
}
|
||||
|
|
|
|||
64
src/app/api/websites/[websiteId]/export/route.ts
Normal file
64
src/app/api/websites/[websiteId]/export/route.ts
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
import { z } from 'zod';
|
||||
import JSZip from 'jszip';
|
||||
import Papa from 'papaparse';
|
||||
import { getQueryFilters, parseRequest } from '@/lib/request';
|
||||
import { unauthorized, json } from '@/lib/response';
|
||||
import { canViewWebsite } from '@/lib/auth';
|
||||
import { pagingParams, dateRangeParams } from '@/lib/schema';
|
||||
import { getEventMetrics, getPageviewMetrics, getSessionMetrics } from '@/queries';
|
||||
|
||||
export async function GET(
|
||||
request: Request,
|
||||
{ params }: { params: Promise<{ websiteId: string }> },
|
||||
) {
|
||||
const schema = z.object({
|
||||
...dateRangeParams,
|
||||
...pagingParams,
|
||||
});
|
||||
|
||||
const { auth, query, error } = await parseRequest(request, schema);
|
||||
|
||||
if (error) {
|
||||
return error();
|
||||
}
|
||||
|
||||
const { websiteId } = await params;
|
||||
|
||||
if (!(await canViewWebsite(auth, websiteId))) {
|
||||
return unauthorized();
|
||||
}
|
||||
|
||||
const filters = await getQueryFilters(query, websiteId);
|
||||
|
||||
const [events, pages, referrers, browsers, os, devices, countries] = await Promise.all([
|
||||
getEventMetrics(websiteId, { type: 'event' }, filters),
|
||||
getPageviewMetrics(websiteId, { type: 'path' }, filters),
|
||||
getPageviewMetrics(websiteId, { type: 'referrer' }, filters),
|
||||
getSessionMetrics(websiteId, { type: 'browser' }, filters),
|
||||
getSessionMetrics(websiteId, { type: 'os' }, filters),
|
||||
getSessionMetrics(websiteId, { type: 'device' }, filters),
|
||||
getSessionMetrics(websiteId, { type: 'country' }, filters),
|
||||
]);
|
||||
|
||||
const zip = new JSZip();
|
||||
|
||||
const parse = (data: any) => {
|
||||
return Papa.unparse(data, {
|
||||
header: true,
|
||||
skipEmptyLines: true,
|
||||
});
|
||||
};
|
||||
|
||||
zip.file('events.csv', parse(events));
|
||||
zip.file('pages.csv', parse(pages));
|
||||
zip.file('referrers.csv', parse(referrers));
|
||||
zip.file('browsers.csv', parse(browsers));
|
||||
zip.file('os.csv', parse(os));
|
||||
zip.file('devices.csv', parse(devices));
|
||||
zip.file('countries.csv', parse(countries));
|
||||
|
||||
const content = await zip.generateAsync({ type: 'nodebuffer' });
|
||||
const base64 = content.toString('base64');
|
||||
|
||||
return json({ zip: base64 });
|
||||
}
|
||||
|
|
@ -13,7 +13,12 @@ import {
|
|||
} from '@/lib/constants';
|
||||
import { parseRequest, getQueryFilters } from '@/lib/request';
|
||||
import { json, unauthorized, badRequest } from '@/lib/response';
|
||||
import { getPageviewMetrics, getSessionMetrics, getChannelMetrics } from '@/queries';
|
||||
import {
|
||||
getEventMetrics,
|
||||
getPageviewMetrics,
|
||||
getSessionMetrics,
|
||||
getChannelMetrics,
|
||||
} from '@/queries';
|
||||
import { dateRangeParams, filterParams, searchParams } from '@/lib/schema';
|
||||
|
||||
export async function GET(
|
||||
|
|
@ -71,7 +76,13 @@ export async function GET(
|
|||
}
|
||||
|
||||
if (EVENT_COLUMNS.includes(type)) {
|
||||
const data = await getPageviewMetrics(websiteId, { type, limit, offset }, filters);
|
||||
let data;
|
||||
|
||||
if (type === 'event') {
|
||||
data = await getEventMetrics(websiteId, { type, limit, offset }, filters);
|
||||
} else {
|
||||
data = await getPageviewMetrics(websiteId, { type, limit, offset }, filters);
|
||||
}
|
||||
|
||||
return json(data);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue