mirror of
https://github.com/umami-software/umami.git
synced 2026-02-15 01:55:36 +01:00
Converted reports and share routes.
This commit is contained in:
parent
dcac7b7c96
commit
6c9f1ad06b
23 changed files with 574 additions and 5 deletions
91
src/app/api/reports/[reportId]/route.ts
Normal file
91
src/app/api/reports/[reportId]/route.ts
Normal file
|
|
@ -0,0 +1,91 @@
|
||||||
|
import { z } from 'zod';
|
||||||
|
import { parseRequest } from 'lib/request';
|
||||||
|
import { deleteReport, getReport, updateReport } from 'queries';
|
||||||
|
import { canDeleteReport, canUpdateReport, canViewReport } from 'lib/auth';
|
||||||
|
import { unauthorized, json, notFound, ok } from 'lib/response';
|
||||||
|
import { reportTypeParam } from 'lib/schema';
|
||||||
|
|
||||||
|
export async function GET(request: Request, { params }: { params: Promise<{ reportId: string }> }) {
|
||||||
|
const { auth, error } = await parseRequest(request);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return error();
|
||||||
|
}
|
||||||
|
|
||||||
|
const { reportId } = await params;
|
||||||
|
|
||||||
|
const report = await getReport(reportId);
|
||||||
|
|
||||||
|
if (!(await canViewReport(auth, report))) {
|
||||||
|
return unauthorized();
|
||||||
|
}
|
||||||
|
|
||||||
|
report.parameters = JSON.parse(report.parameters);
|
||||||
|
|
||||||
|
return json(report);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function POST(
|
||||||
|
request: Request,
|
||||||
|
{ params }: { params: Promise<{ reportId: string }> },
|
||||||
|
) {
|
||||||
|
const schema = z.object({
|
||||||
|
websiteId: z.string().uuid(),
|
||||||
|
type: reportTypeParam,
|
||||||
|
name: z.string().max(200),
|
||||||
|
description: z.string().max(500),
|
||||||
|
parameters: z.object({}).passthrough(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const { auth, body, error } = await parseRequest(request, schema);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return error();
|
||||||
|
}
|
||||||
|
|
||||||
|
const { reportId } = await params;
|
||||||
|
const { websiteId, type, name, description, parameters } = body;
|
||||||
|
|
||||||
|
const report = await getReport(reportId);
|
||||||
|
|
||||||
|
if (!report) {
|
||||||
|
return notFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(await canUpdateReport(auth, report))) {
|
||||||
|
return unauthorized();
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await updateReport(reportId, {
|
||||||
|
websiteId,
|
||||||
|
userId: auth.user.id,
|
||||||
|
type,
|
||||||
|
name,
|
||||||
|
description,
|
||||||
|
parameters: JSON.stringify(parameters),
|
||||||
|
} as any);
|
||||||
|
|
||||||
|
return json(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function DELETE(
|
||||||
|
request: Request,
|
||||||
|
{ params }: { params: Promise<{ reportId: string }> },
|
||||||
|
) {
|
||||||
|
const { auth, error } = await parseRequest(request);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return error();
|
||||||
|
}
|
||||||
|
|
||||||
|
const { reportId } = await params;
|
||||||
|
const report = await getReport(reportId);
|
||||||
|
|
||||||
|
if (!(await canDeleteReport(auth, report))) {
|
||||||
|
return unauthorized();
|
||||||
|
}
|
||||||
|
|
||||||
|
await deleteReport(reportId);
|
||||||
|
|
||||||
|
return ok();
|
||||||
|
}
|
||||||
50
src/app/api/reports/funnel/route.ts
Normal file
50
src/app/api/reports/funnel/route.ts
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
import { z } from 'zod';
|
||||||
|
import { canViewWebsite } from 'lib/auth';
|
||||||
|
import { unauthorized, json } from 'lib/response';
|
||||||
|
import { parseRequest } from 'lib/request';
|
||||||
|
import { getFunnel } from 'queries';
|
||||||
|
|
||||||
|
export async function POST(request: Request) {
|
||||||
|
const schema = z.object({
|
||||||
|
websiteId: z.string().uuid(),
|
||||||
|
steps: z
|
||||||
|
.array(
|
||||||
|
z.object({
|
||||||
|
type: z.string(),
|
||||||
|
value: z.string(),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.min(2),
|
||||||
|
window: z.number().positive(),
|
||||||
|
dateRange: z.object({
|
||||||
|
startDate: z.date(),
|
||||||
|
endDate: z.date(),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const { auth, body, error } = await parseRequest(request, schema);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return error();
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
websiteId,
|
||||||
|
steps,
|
||||||
|
window,
|
||||||
|
dateRange: { startDate, endDate },
|
||||||
|
} = body;
|
||||||
|
|
||||||
|
if (!(await canViewWebsite(auth, websiteId))) {
|
||||||
|
return unauthorized();
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await getFunnel(websiteId, {
|
||||||
|
startDate: new Date(startDate),
|
||||||
|
endDate: new Date(endDate),
|
||||||
|
steps,
|
||||||
|
windowMinutes: +window,
|
||||||
|
});
|
||||||
|
|
||||||
|
return json(data);
|
||||||
|
}
|
||||||
53
src/app/api/reports/goals/route.ts
Normal file
53
src/app/api/reports/goals/route.ts
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
import { z } from 'zod';
|
||||||
|
import { canViewWebsite } from 'lib/auth';
|
||||||
|
import { unauthorized, json } from 'lib/response';
|
||||||
|
import { parseRequest } from 'lib/request';
|
||||||
|
import { getGoals } from 'queries/analytics/reports/getGoals';
|
||||||
|
|
||||||
|
export async function POST(request: Request) {
|
||||||
|
const schema = z.object({
|
||||||
|
websiteId: z.string().uuid(),
|
||||||
|
dateRange: z.object({
|
||||||
|
startDate: z.date(),
|
||||||
|
endDate: z.date(),
|
||||||
|
}),
|
||||||
|
goals: z
|
||||||
|
.array(
|
||||||
|
z.object({
|
||||||
|
type: z.string().regex(/url|event|event-data/),
|
||||||
|
value: z.string(),
|
||||||
|
goal: z.number(),
|
||||||
|
operator: z
|
||||||
|
.string()
|
||||||
|
.regex(/count|sum|average/)
|
||||||
|
.refine(data => data['type'] === 'event-data'),
|
||||||
|
property: z.string().refine(data => data['type'] === 'event-data'),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.min(1),
|
||||||
|
});
|
||||||
|
|
||||||
|
const { auth, body, error } = await parseRequest(request, schema);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return error();
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
websiteId,
|
||||||
|
dateRange: { startDate, endDate },
|
||||||
|
goals,
|
||||||
|
} = body;
|
||||||
|
|
||||||
|
if (!(await canViewWebsite(auth, websiteId))) {
|
||||||
|
return unauthorized();
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await getGoals(websiteId, {
|
||||||
|
startDate: new Date(startDate),
|
||||||
|
endDate: new Date(endDate),
|
||||||
|
goals,
|
||||||
|
});
|
||||||
|
|
||||||
|
return json(data);
|
||||||
|
}
|
||||||
71
src/app/api/reports/insights/route.ts
Normal file
71
src/app/api/reports/insights/route.ts
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
import { z } from 'zod';
|
||||||
|
import { canViewWebsite } from 'lib/auth';
|
||||||
|
import { unauthorized, json } from 'lib/response';
|
||||||
|
import { parseRequest } from 'lib/request';
|
||||||
|
import { getInsights } from 'queries';
|
||||||
|
|
||||||
|
function convertFilters(filters: any[]) {
|
||||||
|
return filters.reduce((obj, filter) => {
|
||||||
|
obj[filter.name] = filter;
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function POST(request: Request) {
|
||||||
|
const schema = z.object({
|
||||||
|
websiteId: z.string().uuid(),
|
||||||
|
dateRange: z.object({
|
||||||
|
startDate: z.date(),
|
||||||
|
endDate: z.date(),
|
||||||
|
}),
|
||||||
|
fields: z
|
||||||
|
.array(
|
||||||
|
z.object({
|
||||||
|
name: z.string(),
|
||||||
|
type: z.string(),
|
||||||
|
label: z.string(),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.min(1),
|
||||||
|
filters: z.array(
|
||||||
|
z.object({
|
||||||
|
name: z.string(),
|
||||||
|
type: z.string(),
|
||||||
|
operator: z.string(),
|
||||||
|
value: z.string(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
groups: z.array(
|
||||||
|
z.object({
|
||||||
|
name: z.string(),
|
||||||
|
type: z.string(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
const { auth, body, error } = await parseRequest(request, schema);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return error();
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
websiteId,
|
||||||
|
dateRange: { startDate, endDate },
|
||||||
|
fields,
|
||||||
|
filters,
|
||||||
|
} = body;
|
||||||
|
|
||||||
|
if (!(await canViewWebsite(auth, websiteId))) {
|
||||||
|
return unauthorized();
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await getInsights(websiteId, fields, {
|
||||||
|
...convertFilters(filters),
|
||||||
|
startDate: new Date(startDate),
|
||||||
|
endDate: new Date(endDate),
|
||||||
|
});
|
||||||
|
|
||||||
|
return json(data);
|
||||||
|
}
|
||||||
46
src/app/api/reports/journey/route.ts
Normal file
46
src/app/api/reports/journey/route.ts
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
import { z } from 'zod';
|
||||||
|
import { canViewWebsite } from 'lib/auth';
|
||||||
|
import { unauthorized, json } from 'lib/response';
|
||||||
|
import { parseRequest } from 'lib/request';
|
||||||
|
import { getJourney } from 'queries';
|
||||||
|
|
||||||
|
export async function POST(request: Request) {
|
||||||
|
const schema = z.object({
|
||||||
|
websiteId: z.string().uuid(),
|
||||||
|
dateRange: z.object({
|
||||||
|
startDate: z.date(),
|
||||||
|
endDate: z.date(),
|
||||||
|
}),
|
||||||
|
steps: z.number().min(3).max(7),
|
||||||
|
startStep: z.string(),
|
||||||
|
endStep: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const { auth, body, error } = await parseRequest(request, schema);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return error();
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
websiteId,
|
||||||
|
dateRange: { startDate, endDate },
|
||||||
|
steps,
|
||||||
|
startStep,
|
||||||
|
endStep,
|
||||||
|
} = body;
|
||||||
|
|
||||||
|
if (!(await canViewWebsite(auth, websiteId))) {
|
||||||
|
return unauthorized();
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await getJourney(websiteId, {
|
||||||
|
startDate: new Date(startDate),
|
||||||
|
endDate: new Date(endDate),
|
||||||
|
steps,
|
||||||
|
startStep,
|
||||||
|
endStep,
|
||||||
|
});
|
||||||
|
|
||||||
|
return json(data);
|
||||||
|
}
|
||||||
41
src/app/api/reports/retention/route.ts
Normal file
41
src/app/api/reports/retention/route.ts
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
import { z } from 'zod';
|
||||||
|
import { canViewWebsite } from 'lib/auth';
|
||||||
|
import { unauthorized, json } from 'lib/response';
|
||||||
|
import { parseRequest } from 'lib/request';
|
||||||
|
import { getRetention } from 'queries';
|
||||||
|
import { timezoneParam } from 'lib/schema';
|
||||||
|
|
||||||
|
export async function POST(request: Request) {
|
||||||
|
const schema = z.object({
|
||||||
|
websiteId: z.string().uuid(),
|
||||||
|
dateRange: z.object({
|
||||||
|
startDate: z.date(),
|
||||||
|
endDate: z.date(),
|
||||||
|
}),
|
||||||
|
timezone: timezoneParam,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { auth, body, error } = await parseRequest(request, schema);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return error();
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
websiteId,
|
||||||
|
dateRange: { startDate, endDate },
|
||||||
|
timezone,
|
||||||
|
} = body;
|
||||||
|
|
||||||
|
if (!(await canViewWebsite(auth, websiteId))) {
|
||||||
|
return unauthorized();
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await getRetention(websiteId, {
|
||||||
|
startDate: new Date(startDate),
|
||||||
|
endDate: new Date(endDate),
|
||||||
|
timezone,
|
||||||
|
});
|
||||||
|
|
||||||
|
return json(data);
|
||||||
|
}
|
||||||
75
src/app/api/reports/revenue/route.ts
Normal file
75
src/app/api/reports/revenue/route.ts
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
import { z } from 'zod';
|
||||||
|
import { canViewWebsite } from 'lib/auth';
|
||||||
|
import { unauthorized, json } from 'lib/response';
|
||||||
|
import { parseRequest } from 'lib/request';
|
||||||
|
import { timezoneParam, unitParam } from 'lib/schema';
|
||||||
|
import { getRevenue } from 'queries/analytics/reports/getRevenue';
|
||||||
|
import { getRevenueValues } from 'queries/analytics/reports/getRevenueValues';
|
||||||
|
|
||||||
|
export async function GET(request: Request) {
|
||||||
|
const schema = z.object({
|
||||||
|
websiteId: z.string().uuid(),
|
||||||
|
dateRange: z.object({
|
||||||
|
startDate: z.date(),
|
||||||
|
endDate: z.date(),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const { auth, query, error } = await parseRequest(request, schema);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return error();
|
||||||
|
}
|
||||||
|
|
||||||
|
const { websiteId, startDate, endDate } = query;
|
||||||
|
|
||||||
|
if (!(await canViewWebsite(auth, websiteId))) {
|
||||||
|
return unauthorized();
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await getRevenueValues(websiteId, {
|
||||||
|
startDate: new Date(startDate),
|
||||||
|
endDate: new Date(endDate),
|
||||||
|
});
|
||||||
|
|
||||||
|
return json(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function POST(request: Request) {
|
||||||
|
const schema = z.object({
|
||||||
|
websiteId: z.string().uuid(),
|
||||||
|
dateRange: z.object({
|
||||||
|
startDate: z.date(),
|
||||||
|
endDate: z.date(),
|
||||||
|
unit: unitParam,
|
||||||
|
}),
|
||||||
|
timezone: timezoneParam,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { auth, body, error } = await parseRequest(request, schema);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return error();
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
websiteId,
|
||||||
|
currency,
|
||||||
|
timezone,
|
||||||
|
dateRange: { startDate, endDate, unit },
|
||||||
|
} = body;
|
||||||
|
|
||||||
|
if (!(await canViewWebsite(auth, websiteId))) {
|
||||||
|
return unauthorized();
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await getRevenue(websiteId, {
|
||||||
|
startDate: new Date(startDate),
|
||||||
|
endDate: new Date(endDate),
|
||||||
|
unit,
|
||||||
|
timezone,
|
||||||
|
currency,
|
||||||
|
});
|
||||||
|
|
||||||
|
return json(data);
|
||||||
|
}
|
||||||
73
src/app/api/reports/route.ts
Normal file
73
src/app/api/reports/route.ts
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
import { z } from 'zod';
|
||||||
|
import { pagingParams } from 'lib/schema';
|
||||||
|
import { parseRequest } from 'lib/request';
|
||||||
|
import { canViewTeam, canViewWebsite } from 'lib/auth';
|
||||||
|
import { unauthorized, json } from 'lib/response';
|
||||||
|
import { getReports } from 'queries/prisma/report';
|
||||||
|
|
||||||
|
export async function GET(request: Request) {
|
||||||
|
const schema = z.object({
|
||||||
|
...pagingParams,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { auth, query, error } = await parseRequest(request, schema);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return error();
|
||||||
|
}
|
||||||
|
|
||||||
|
const { page, search, pageSize, websiteId, teamId } = query;
|
||||||
|
const userId = auth.user.id;
|
||||||
|
const filters = {
|
||||||
|
page,
|
||||||
|
pageSize,
|
||||||
|
search,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (
|
||||||
|
(websiteId && !(await canViewWebsite(auth, websiteId))) ||
|
||||||
|
(teamId && !(await canViewTeam(auth, teamId)))
|
||||||
|
) {
|
||||||
|
return unauthorized();
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await getReports(
|
||||||
|
{
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
...(websiteId ? [{ websiteId }] : []),
|
||||||
|
...(teamId
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
website: {
|
||||||
|
deletedAt: null,
|
||||||
|
teamId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: []),
|
||||||
|
...(userId && !websiteId && !teamId
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
website: {
|
||||||
|
deletedAt: null,
|
||||||
|
userId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: []),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
website: {
|
||||||
|
select: {
|
||||||
|
domain: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
filters,
|
||||||
|
);
|
||||||
|
|
||||||
|
return json(data);
|
||||||
|
}
|
||||||
40
src/app/api/reports/utm/route.ts
Normal file
40
src/app/api/reports/utm/route.ts
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
import { z } from 'zod';
|
||||||
|
import { canViewWebsite } from 'lib/auth';
|
||||||
|
import { unauthorized, json } from 'lib/response';
|
||||||
|
import { parseRequest } from 'lib/request';
|
||||||
|
import { getUTM } from 'queries';
|
||||||
|
import { timezoneParam } from 'lib/schema';
|
||||||
|
|
||||||
|
export async function POST(request: Request) {
|
||||||
|
const schema = z.object({
|
||||||
|
websiteId: z.string().uuid(),
|
||||||
|
dateRange: z.object({
|
||||||
|
startDate: z.date(),
|
||||||
|
endDate: z.date(),
|
||||||
|
timezone: timezoneParam,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const { auth, body, error } = await parseRequest(request, schema);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return error();
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
websiteId,
|
||||||
|
dateRange: { startDate, endDate, timezone },
|
||||||
|
} = body;
|
||||||
|
|
||||||
|
if (!(await canViewWebsite(auth, websiteId))) {
|
||||||
|
return unauthorized();
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await getUTM(websiteId, {
|
||||||
|
startDate: new Date(startDate),
|
||||||
|
endDate: new Date(endDate),
|
||||||
|
timezone,
|
||||||
|
});
|
||||||
|
|
||||||
|
return json(data);
|
||||||
|
}
|
||||||
19
src/app/api/share/[shareId]/route.ts
Normal file
19
src/app/api/share/[shareId]/route.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { json, notFound } from 'lib/response';
|
||||||
|
import { getSharedWebsite } from 'queries';
|
||||||
|
import { createToken } from 'next-basics';
|
||||||
|
import { secret } from 'lib/crypto';
|
||||||
|
|
||||||
|
export async function GET(request: Request, { params }: { params: Promise<{ shareId: string }> }) {
|
||||||
|
const { shareId } = await params;
|
||||||
|
|
||||||
|
const website = await getSharedWebsite(shareId);
|
||||||
|
|
||||||
|
if (!website) {
|
||||||
|
return notFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = { websiteId: website.id };
|
||||||
|
const token = createToken(data, secret());
|
||||||
|
|
||||||
|
return json({ ...data, token });
|
||||||
|
}
|
||||||
|
|
@ -243,7 +243,7 @@ async function pagedQuery<T>(model: string, criteria: T, pageParams: PageParams)
|
||||||
const data = await prisma.client[model].findMany({
|
const data = await prisma.client[model].findMany({
|
||||||
...criteria,
|
...criteria,
|
||||||
...{
|
...{
|
||||||
...(size > 0 && { take: +size, skip: +size * (page - 1) }),
|
...(size > 0 && { take: +size, skip: +size * (+page - 1) }),
|
||||||
...(orderBy && {
|
...(orderBy && {
|
||||||
orderBy: [
|
orderBy: [
|
||||||
{
|
{
|
||||||
|
|
@ -266,7 +266,7 @@ async function pagedRawQuery(
|
||||||
) {
|
) {
|
||||||
const { page = 1, pageSize, orderBy, sortDescending = false } = pageParams;
|
const { page = 1, pageSize, orderBy, sortDescending = false } = pageParams;
|
||||||
const size = +pageSize || DEFAULT_PAGE_SIZE;
|
const size = +pageSize || DEFAULT_PAGE_SIZE;
|
||||||
const offset = +size * (page - 1);
|
const offset = +size * (+page - 1);
|
||||||
const direction = sortDescending ? 'desc' : 'asc';
|
const direction = sortDescending ? 'desc' : 'asc';
|
||||||
|
|
||||||
const statements = [
|
const statements = [
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,17 @@ export const unitParam = z.string().refine(value => UNIT_TYPES.includes(value),
|
||||||
message: 'Invalid unit',
|
message: 'Invalid unit',
|
||||||
});
|
});
|
||||||
|
|
||||||
export const roleParam = z.string().regex(/team-member|team-view-only|team-manager/);
|
export const roleParam = z.enum(['team-member', 'team-view-only', 'team-manager']);
|
||||||
|
|
||||||
|
export const reportTypeParam = z.enum([
|
||||||
|
'funnel',
|
||||||
|
'insights',
|
||||||
|
'retention',
|
||||||
|
'utm',
|
||||||
|
'goals',
|
||||||
|
'journey',
|
||||||
|
'revenue',
|
||||||
|
]);
|
||||||
|
|
||||||
export const filterParams = {
|
export const filterParams = {
|
||||||
url: z.string().optional(),
|
url: z.string().optional(),
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,8 @@ export type ReportType = ObjectValues<typeof REPORT_TYPES>;
|
||||||
|
|
||||||
export interface PageParams {
|
export interface PageParams {
|
||||||
search?: string;
|
search?: string;
|
||||||
page?: number;
|
page?: string;
|
||||||
pageSize?: number;
|
pageSize?: string;
|
||||||
orderBy?: string;
|
orderBy?: string;
|
||||||
sortDescending?: boolean;
|
sortDescending?: boolean;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue