migrate to app dir and ssr
This commit is contained in:
@@ -4,29 +4,28 @@ import { getChartSql } from '@/server/chart-sql/getChartSql';
|
||||
import { isJsonPath, selectJsonPath } from '@/server/chart-sql/helpers';
|
||||
import { db } from '@/server/db';
|
||||
import { getUniqueEvents } from '@/server/services/event.service';
|
||||
import { getProjectBySlug } from '@/server/services/project.service';
|
||||
import type {
|
||||
IChartEvent,
|
||||
IChartRange,
|
||||
IGetChartDataInput,
|
||||
IInterval,
|
||||
} from '@/types';
|
||||
import { alphabetIds } from '@/utils/constants';
|
||||
import { getDaysOldDate } from '@/utils/date';
|
||||
import { average, isFloat, round, sum } from '@/utils/math';
|
||||
import { average, round, sum } from '@/utils/math';
|
||||
import { toDots } from '@/utils/object';
|
||||
import { zChartInputWithDates } from '@/utils/validation';
|
||||
import { last, pipe, sort, uniq } from 'ramda';
|
||||
import { pipe, sort, uniq } from 'ramda';
|
||||
import { z } from 'zod';
|
||||
|
||||
export const chartRouter = createTRPCRouter({
|
||||
events: protectedProcedure
|
||||
.input(z.object({ projectSlug: z.string() }))
|
||||
.query(async ({ input: { projectSlug } }) => {
|
||||
const project = await getProjectBySlug(projectSlug);
|
||||
.input(z.object({ projectId: z.string() }))
|
||||
.query(async ({ input: { projectId } }) => {
|
||||
const events = await cache.getOr(
|
||||
`events_${project.id}`,
|
||||
`events_${projectId}`,
|
||||
1000 * 60 * 60 * 24,
|
||||
() => getUniqueEvents({ projectId: project.id })
|
||||
() => getUniqueEvents({ projectId: projectId })
|
||||
);
|
||||
|
||||
return [
|
||||
@@ -38,17 +37,16 @@ export const chartRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
properties: protectedProcedure
|
||||
.input(z.object({ event: z.string().optional(), projectSlug: z.string() }))
|
||||
.query(async ({ input: { projectSlug, event } }) => {
|
||||
const project = await getProjectBySlug(projectSlug);
|
||||
.input(z.object({ event: z.string().optional(), projectId: z.string() }))
|
||||
.query(async ({ input: { projectId, event } }) => {
|
||||
const events = await cache.getOr(
|
||||
`events_${project.id}_${event ?? 'all'}`,
|
||||
`events_${projectId}_${event ?? 'all'}`,
|
||||
1000 * 60 * 60,
|
||||
() =>
|
||||
db.event.findMany({
|
||||
take: 500,
|
||||
where: {
|
||||
project_id: project.id,
|
||||
project_id: projectId,
|
||||
...(event
|
||||
? {
|
||||
name: event,
|
||||
@@ -78,19 +76,16 @@ export const chartRouter = createTRPCRouter({
|
||||
z.object({
|
||||
event: z.string(),
|
||||
property: z.string(),
|
||||
projectSlug: z.string(),
|
||||
projectId: z.string(),
|
||||
})
|
||||
)
|
||||
.query(async ({ input: { event, property, projectSlug } }) => {
|
||||
.query(async ({ input: { event, property, projectId } }) => {
|
||||
const intervalInDays = 180;
|
||||
const project = await getProjectBySlug(projectSlug);
|
||||
if (isJsonPath(property)) {
|
||||
const events = await db.$queryRawUnsafe<{ value: string }[]>(
|
||||
`SELECT ${selectJsonPath(
|
||||
property
|
||||
)} AS value from events WHERE project_id = '${
|
||||
project.id
|
||||
}' AND name = '${event}' AND "createdAt" >= NOW() - INTERVAL '${intervalInDays} days'`
|
||||
)} AS value from events WHERE project_id = '${projectId}' AND name = '${event}' AND "createdAt" >= NOW() - INTERVAL '${intervalInDays} days'`
|
||||
);
|
||||
|
||||
return {
|
||||
@@ -99,7 +94,7 @@ export const chartRouter = createTRPCRouter({
|
||||
} else {
|
||||
const events = await db.event.findMany({
|
||||
where: {
|
||||
project_id: project.id,
|
||||
project_id: projectId,
|
||||
name: event,
|
||||
[property]: {
|
||||
not: null,
|
||||
@@ -123,8 +118,8 @@ export const chartRouter = createTRPCRouter({
|
||||
}),
|
||||
|
||||
chart: protectedProcedure
|
||||
.input(zChartInputWithDates.merge(z.object({ projectSlug: z.string() })))
|
||||
.query(async ({ input: { projectSlug, events, ...input } }) => {
|
||||
.input(zChartInputWithDates.merge(z.object({ projectId: z.string() })))
|
||||
.query(async ({ input: { projectId, events, ...input } }) => {
|
||||
const { startDate, endDate } =
|
||||
input.startDate && input.endDate
|
||||
? {
|
||||
@@ -132,18 +127,16 @@ export const chartRouter = createTRPCRouter({
|
||||
endDate: input.endDate,
|
||||
}
|
||||
: getDatesFromRange(input.range);
|
||||
const project = await getProjectBySlug(projectSlug);
|
||||
const series: Awaited<ReturnType<typeof getChartData>> = [];
|
||||
for (const event of events) {
|
||||
series.push(
|
||||
...(await getChartData({
|
||||
...input,
|
||||
startDate,
|
||||
endDate,
|
||||
event,
|
||||
projectId: project.id,
|
||||
}))
|
||||
);
|
||||
const result = await getChartData({
|
||||
...input,
|
||||
startDate,
|
||||
endDate,
|
||||
event,
|
||||
projectId: projectId,
|
||||
});
|
||||
series.push(...result);
|
||||
}
|
||||
|
||||
const sorted = [...series].sort((a, b) => {
|
||||
@@ -152,13 +145,18 @@ export const chartRouter = createTRPCRouter({
|
||||
const sumB = b.data.reduce((acc, item) => acc + item.count, 0);
|
||||
return sumB - sumA;
|
||||
} else {
|
||||
return b.metrics.total - a.metrics.total;
|
||||
return b.metrics.sum - a.metrics.sum;
|
||||
}
|
||||
});
|
||||
|
||||
const meta = {
|
||||
highest: sorted[0]?.metrics.total ?? 0,
|
||||
lowest: last(sorted)?.metrics.total ?? 0,
|
||||
const metrics = {
|
||||
max: Math.max(...sorted.map((item) => item.metrics.max)),
|
||||
min: Math.min(...sorted.map((item) => item.metrics.min)),
|
||||
sum: sum(sorted.map((item) => item.metrics.sum, 0)),
|
||||
averge: round(
|
||||
average(sorted.map((item) => item.metrics.average, 0)),
|
||||
2
|
||||
),
|
||||
};
|
||||
|
||||
return {
|
||||
@@ -166,9 +164,9 @@ export const chartRouter = createTRPCRouter({
|
||||
series.reduce(
|
||||
(acc, item) => {
|
||||
if (acc[item.event.id]) {
|
||||
acc[item.event.id] += item.metrics.total;
|
||||
acc[item.event.id] += item.metrics.sum;
|
||||
} else {
|
||||
acc[item.event.id] = item.metrics.total;
|
||||
acc[item.event.id] = item.metrics.sum;
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
@@ -180,8 +178,12 @@ export const chartRouter = createTRPCRouter({
|
||||
})),
|
||||
series: sorted.map((item) => ({
|
||||
...item,
|
||||
meta,
|
||||
metrics: {
|
||||
...item.metrics,
|
||||
totalMetrics: metrics,
|
||||
},
|
||||
})),
|
||||
metrics,
|
||||
};
|
||||
}),
|
||||
});
|
||||
@@ -282,36 +284,47 @@ async function getChartData(payload: IGetChartDataInput) {
|
||||
);
|
||||
|
||||
return Object.keys(series).map((key) => {
|
||||
const legend = payload.breakdowns.length
|
||||
? key
|
||||
: getEventLegend(payload.event);
|
||||
const data = series[key] ?? [];
|
||||
// If we have breakdowns, we want to use the breakdown key as the legend
|
||||
// But only if it successfully broke it down, otherwise we use the getEventLabel
|
||||
const legend =
|
||||
payload.breakdowns.length && !alphabetIds.includes(key as 'A')
|
||||
? key
|
||||
: getEventLegend(payload.event);
|
||||
const data =
|
||||
payload.chartType === 'area' ||
|
||||
payload.chartType === 'linear' ||
|
||||
payload.chartType === 'histogram' ||
|
||||
payload.chartType === 'metric'
|
||||
? fillEmptySpotsInTimeline(
|
||||
series[key] ?? [],
|
||||
payload.interval,
|
||||
payload.startDate,
|
||||
payload.endDate
|
||||
).map((item) => {
|
||||
return {
|
||||
label: legend,
|
||||
count: round(item.count),
|
||||
date: new Date(item.date).toISOString(),
|
||||
};
|
||||
})
|
||||
: (series[key] ?? []).map((item) => ({
|
||||
label: item.label,
|
||||
count: round(item.count),
|
||||
date: new Date(item.date).toISOString(),
|
||||
}));
|
||||
|
||||
const counts = data.map((item) => item.count);
|
||||
|
||||
return {
|
||||
name: legend,
|
||||
event: {
|
||||
id: payload.event.id,
|
||||
name: payload.event.name,
|
||||
},
|
||||
event: payload.event,
|
||||
metrics: {
|
||||
total: sum(data.map((item) => item.count)),
|
||||
average: round(average(data.map((item) => item.count))),
|
||||
sum: sum(counts),
|
||||
average: round(average(counts)),
|
||||
max: Math.max(...counts),
|
||||
min: Math.min(...counts),
|
||||
},
|
||||
data:
|
||||
payload.chartType === 'linear' || payload.chartType === 'histogram'
|
||||
? fillEmptySpotsInTimeline(
|
||||
data,
|
||||
payload.interval,
|
||||
payload.startDate,
|
||||
payload.endDate
|
||||
).map((item) => {
|
||||
return {
|
||||
label: legend,
|
||||
count: round(item.count),
|
||||
date: new Date(item.date).toISOString(),
|
||||
};
|
||||
})
|
||||
: [],
|
||||
data,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2,21 +2,19 @@ import { randomUUID } from 'crypto';
|
||||
import { createTRPCRouter, protectedProcedure } from '@/server/api/trpc';
|
||||
import { db } from '@/server/db';
|
||||
import { hashPassword } from '@/server/services/hash.service';
|
||||
import { getOrganizationBySlug } from '@/server/services/organization.service';
|
||||
import { z } from 'zod';
|
||||
|
||||
export const clientRouter = createTRPCRouter({
|
||||
list: protectedProcedure
|
||||
.input(
|
||||
z.object({
|
||||
organizationSlug: z.string(),
|
||||
organizationId: z.string(),
|
||||
})
|
||||
)
|
||||
.query(async ({ input }) => {
|
||||
const organization = await getOrganizationBySlug(input.organizationSlug);
|
||||
.query(async ({ input: { organizationId } }) => {
|
||||
return db.client.findMany({
|
||||
where: {
|
||||
organization_id: organization.id,
|
||||
organization_id: organizationId,
|
||||
},
|
||||
include: {
|
||||
project: true,
|
||||
@@ -60,16 +58,15 @@ export const clientRouter = createTRPCRouter({
|
||||
z.object({
|
||||
name: z.string(),
|
||||
projectId: z.string(),
|
||||
organizationSlug: z.string(),
|
||||
organizationId: z.string(),
|
||||
withCors: z.boolean().default(true),
|
||||
})
|
||||
)
|
||||
.mutation(async ({ input }) => {
|
||||
const organization = await getOrganizationBySlug(input.organizationSlug);
|
||||
const secret = randomUUID();
|
||||
const client = await db.client.create({
|
||||
data: {
|
||||
organization_id: organization.id,
|
||||
organization_id: input.organizationId,
|
||||
project_id: input.projectId,
|
||||
name: input.name,
|
||||
secret: input.withCors ? null : await hashPassword(secret),
|
||||
|
||||
@@ -1,75 +1,72 @@
|
||||
import { createTRPCRouter, protectedProcedure } from '@/server/api/trpc';
|
||||
import { db } from '@/server/db';
|
||||
import { getDashboardBySlug } from '@/server/services/dashboard.service';
|
||||
import { getProjectBySlug } from '@/server/services/project.service';
|
||||
import { slug } from '@/utils/slug';
|
||||
import { PrismaError } from 'prisma-error-enum';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { Prisma } from '@mixan/db';
|
||||
import type { Prisma } from '@mixan/db';
|
||||
|
||||
export const dashboardRouter = createTRPCRouter({
|
||||
get: protectedProcedure
|
||||
.input(
|
||||
z
|
||||
.object({
|
||||
slug: z.string(),
|
||||
})
|
||||
.or(z.object({ id: z.string() }))
|
||||
)
|
||||
.input(z.object({ id: z.string() }))
|
||||
.query(async ({ input }) => {
|
||||
if ('id' in input) {
|
||||
return db.dashboard.findUnique({
|
||||
where: {
|
||||
id: input.id,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
return getDashboardBySlug(input.slug);
|
||||
}
|
||||
return db.dashboard.findUnique({
|
||||
where: {
|
||||
id: input.id,
|
||||
},
|
||||
});
|
||||
}),
|
||||
list: protectedProcedure
|
||||
.input(
|
||||
z
|
||||
.object({
|
||||
projectSlug: z.string(),
|
||||
projectId: z.string(),
|
||||
})
|
||||
.or(
|
||||
z.object({
|
||||
projectId: z.string(),
|
||||
organizationId: z.string(),
|
||||
})
|
||||
)
|
||||
)
|
||||
.query(async ({ input }) => {
|
||||
let projectId = null;
|
||||
if ('projectId' in input) {
|
||||
projectId = input.projectId;
|
||||
return db.dashboard.findMany({
|
||||
where: {
|
||||
project_id: input.projectId,
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: 'desc',
|
||||
},
|
||||
include: {
|
||||
project: true,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
projectId = (await getProjectBySlug(input.projectSlug)).id;
|
||||
return db.dashboard.findMany({
|
||||
where: {
|
||||
project: {
|
||||
organization_id: input.organizationId,
|
||||
},
|
||||
},
|
||||
include: {
|
||||
project: true,
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: 'desc',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return db.dashboard.findMany({
|
||||
where: {
|
||||
project_id: projectId,
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: 'desc',
|
||||
},
|
||||
});
|
||||
}),
|
||||
create: protectedProcedure
|
||||
.input(
|
||||
z.object({
|
||||
name: z.string(),
|
||||
projectSlug: z.string(),
|
||||
projectId: z.string(),
|
||||
})
|
||||
)
|
||||
.mutation(async ({ input: { projectSlug, name } }) => {
|
||||
const project = await getProjectBySlug(projectSlug);
|
||||
.mutation(async ({ input: { projectId, name } }) => {
|
||||
return db.dashboard.create({
|
||||
data: {
|
||||
slug: slug(name),
|
||||
project_id: project.id,
|
||||
project_id: projectId,
|
||||
name,
|
||||
},
|
||||
});
|
||||
@@ -95,17 +92,33 @@ export const dashboardRouter = createTRPCRouter({
|
||||
.input(
|
||||
z.object({
|
||||
id: z.string(),
|
||||
forceDelete: z.boolean().optional(),
|
||||
})
|
||||
)
|
||||
.mutation(async ({ input: { id } }) => {
|
||||
.mutation(async ({ input: { id, forceDelete } }) => {
|
||||
try {
|
||||
if (forceDelete) {
|
||||
await db.report.deleteMany({
|
||||
where: {
|
||||
dashboard_id: id,
|
||||
},
|
||||
});
|
||||
}
|
||||
await db.recentDashboards.deleteMany({
|
||||
where: {
|
||||
dashboard_id: id,
|
||||
},
|
||||
});
|
||||
await db.dashboard.delete({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
} catch (e) {
|
||||
// Below does not work...
|
||||
// error instanceof Prisma.PrismaClientKnownRequestError
|
||||
if (typeof e === 'object' && e && 'code' in e) {
|
||||
const error = e as Prisma.PrismaClientKnownRequestError;
|
||||
switch (error.code) {
|
||||
case PrismaError.ForeignConstraintViolation:
|
||||
throw new Error(
|
||||
|
||||
@@ -2,29 +2,37 @@ import { createTRPCRouter, protectedProcedure } from '@/server/api/trpc';
|
||||
import { db } from '@/server/db';
|
||||
import { z } from 'zod';
|
||||
|
||||
import type { Event, Profile } from '@mixan/db';
|
||||
|
||||
function transformEvent(
|
||||
event: Event & {
|
||||
profile: Profile;
|
||||
}
|
||||
) {
|
||||
return {
|
||||
...event,
|
||||
properties: event.properties as Record<string, unknown>,
|
||||
};
|
||||
}
|
||||
|
||||
export const eventRouter = createTRPCRouter({
|
||||
list: protectedProcedure
|
||||
.input(
|
||||
z.object({
|
||||
projectSlug: z.string(),
|
||||
projectId: z.string(),
|
||||
take: z.number().default(100),
|
||||
skip: z.number().default(0),
|
||||
profileId: z.string().optional(),
|
||||
events: z.array(z.string()).optional(),
|
||||
})
|
||||
)
|
||||
.query(
|
||||
async ({ input: { take, skip, projectSlug, profileId, events } }) => {
|
||||
const project = await db.project.findUniqueOrThrow({
|
||||
where: {
|
||||
slug: projectSlug,
|
||||
},
|
||||
});
|
||||
return db.event.findMany({
|
||||
.query(async ({ input: { take, skip, projectId, profileId, events } }) => {
|
||||
return db.event
|
||||
.findMany({
|
||||
take,
|
||||
skip,
|
||||
where: {
|
||||
project_id: project.id,
|
||||
project_id: projectId,
|
||||
profile_id: profileId,
|
||||
...(events && events.length > 0
|
||||
? {
|
||||
@@ -40,7 +48,7 @@ export const eventRouter = createTRPCRouter({
|
||||
include: {
|
||||
profile: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
),
|
||||
})
|
||||
.then((events) => events.map(transformEvent));
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -1,10 +1,20 @@
|
||||
import { createTRPCRouter, protectedProcedure } from '@/server/api/trpc';
|
||||
import { db } from '@/server/db';
|
||||
import { getOrganizationBySlug } from '@/server/services/organization.service';
|
||||
import { slug } from '@/utils/slug';
|
||||
import { getOrganizationById } from '@/server/services/organization.service';
|
||||
import { z } from 'zod';
|
||||
|
||||
export const organizationRouter = createTRPCRouter({
|
||||
list: protectedProcedure.query(({ ctx }) => {
|
||||
return db.organization.findMany({
|
||||
where: {
|
||||
users: {
|
||||
some: {
|
||||
id: ctx.session.user.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}),
|
||||
first: protectedProcedure.query(({ ctx }) => {
|
||||
return db.organization.findFirst({
|
||||
where: {
|
||||
@@ -19,11 +29,11 @@ export const organizationRouter = createTRPCRouter({
|
||||
get: protectedProcedure
|
||||
.input(
|
||||
z.object({
|
||||
slug: z.string(),
|
||||
id: z.string(),
|
||||
})
|
||||
)
|
||||
.query(({ input }) => {
|
||||
return getOrganizationBySlug(input.slug);
|
||||
return getOrganizationById(input.id);
|
||||
}),
|
||||
update: protectedProcedure
|
||||
.input(
|
||||
@@ -39,7 +49,6 @@ export const organizationRouter = createTRPCRouter({
|
||||
},
|
||||
data: {
|
||||
name: input.name,
|
||||
slug: slug(input.name),
|
||||
},
|
||||
});
|
||||
}),
|
||||
|
||||
@@ -6,22 +6,42 @@ export const profileRouter = createTRPCRouter({
|
||||
list: protectedProcedure
|
||||
.input(
|
||||
z.object({
|
||||
projectSlug: z.string(),
|
||||
query: z.string().nullable(),
|
||||
projectId: z.string(),
|
||||
take: z.number().default(100),
|
||||
skip: z.number().default(0),
|
||||
})
|
||||
)
|
||||
.query(async ({ input: { take, skip, projectSlug } }) => {
|
||||
const project = await db.project.findUniqueOrThrow({
|
||||
where: {
|
||||
slug: projectSlug,
|
||||
},
|
||||
});
|
||||
.query(async ({ input: { take, skip, projectId, query } }) => {
|
||||
return db.profile.findMany({
|
||||
take,
|
||||
skip,
|
||||
where: {
|
||||
project_id: project.id,
|
||||
project_id: projectId,
|
||||
...(query
|
||||
? {
|
||||
OR: [
|
||||
{
|
||||
first_name: {
|
||||
contains: query,
|
||||
mode: 'insensitive',
|
||||
},
|
||||
},
|
||||
{
|
||||
last_name: {
|
||||
contains: query,
|
||||
mode: 'insensitive',
|
||||
},
|
||||
},
|
||||
{
|
||||
email: {
|
||||
contains: query,
|
||||
mode: 'insensitive',
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: 'desc',
|
||||
|
||||
@@ -1,40 +1,34 @@
|
||||
import { createTRPCRouter, protectedProcedure } from '@/server/api/trpc';
|
||||
import { db } from '@/server/db';
|
||||
import { getOrganizationBySlug } from '@/server/services/organization.service';
|
||||
import { getProjectBySlug } from '@/server/services/project.service';
|
||||
import { db, getId } from '@/server/db';
|
||||
import { slug } from '@/utils/slug';
|
||||
import { z } from 'zod';
|
||||
|
||||
export const projectRouter = createTRPCRouter({
|
||||
list: protectedProcedure
|
||||
.input(
|
||||
z.object({
|
||||
organizationSlug: z.string(),
|
||||
organizationId: z.string().nullable(),
|
||||
})
|
||||
)
|
||||
.query(async ({ input }) => {
|
||||
const organization = await getOrganizationBySlug(input.organizationSlug);
|
||||
.query(async ({ input: { organizationId } }) => {
|
||||
if (organizationId === null) return [];
|
||||
|
||||
return db.project.findMany({
|
||||
where: {
|
||||
organization_id: organization.id,
|
||||
organization_id: organizationId,
|
||||
},
|
||||
});
|
||||
}),
|
||||
get: protectedProcedure
|
||||
.input(
|
||||
z
|
||||
.object({
|
||||
id: z.string(),
|
||||
})
|
||||
.or(z.object({ slug: z.string() }))
|
||||
z.object({
|
||||
id: z.string(),
|
||||
})
|
||||
)
|
||||
.query(({ input }) => {
|
||||
if ('slug' in input) {
|
||||
return getProjectBySlug(input.slug);
|
||||
}
|
||||
|
||||
.query(({ input: { id } }) => {
|
||||
return db.project.findUniqueOrThrow({
|
||||
where: {
|
||||
id: input.id,
|
||||
id,
|
||||
},
|
||||
});
|
||||
}),
|
||||
@@ -58,15 +52,15 @@ export const projectRouter = createTRPCRouter({
|
||||
create: protectedProcedure
|
||||
.input(
|
||||
z.object({
|
||||
name: z.string(),
|
||||
organizationSlug: z.string(),
|
||||
name: z.string().min(1),
|
||||
organizationId: z.string(),
|
||||
})
|
||||
)
|
||||
.mutation(async ({ input }) => {
|
||||
const organization = await getOrganizationBySlug(input.organizationSlug);
|
||||
return db.project.create({
|
||||
data: {
|
||||
organization_id: organization.id,
|
||||
id: await getId('project', input.name),
|
||||
organization_id: input.organizationId,
|
||||
name: input.name,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,58 +1,9 @@
|
||||
import { createTRPCRouter, protectedProcedure } from '@/server/api/trpc';
|
||||
import { db } from '@/server/db';
|
||||
import { getDashboardBySlug } from '@/server/services/dashboard.service';
|
||||
import { getProjectBySlug } from '@/server/services/project.service';
|
||||
import type {
|
||||
IChartBreakdown,
|
||||
IChartEvent,
|
||||
IChartEventFilter,
|
||||
IChartInput,
|
||||
IChartRange,
|
||||
} from '@/types';
|
||||
import { alphabetIds, timeRanges } from '@/utils/constants';
|
||||
import { transformReport } from '@/server/services/reports.service';
|
||||
import { zChartInput } from '@/utils/validation';
|
||||
import { z } from 'zod';
|
||||
|
||||
import type { Report as DbReport } from '@mixan/db';
|
||||
|
||||
function transformFilter(
|
||||
filter: Partial<IChartEventFilter>,
|
||||
index: number
|
||||
): IChartEventFilter {
|
||||
return {
|
||||
id: filter.id ?? alphabetIds[index]!,
|
||||
name: filter.name ?? 'Unknown Filter',
|
||||
operator: filter.operator ?? 'is',
|
||||
value:
|
||||
typeof filter.value === 'string' ? [filter.value] : filter.value ?? [],
|
||||
};
|
||||
}
|
||||
|
||||
function transformEvent(
|
||||
event: Partial<IChartEvent>,
|
||||
index: number
|
||||
): IChartEvent {
|
||||
return {
|
||||
segment: event.segment ?? 'event',
|
||||
filters: (event.filters ?? []).map(transformFilter),
|
||||
id: event.id ?? alphabetIds[index]!,
|
||||
name: event.name || 'unknown_event',
|
||||
displayName: event.displayName,
|
||||
};
|
||||
}
|
||||
|
||||
function transformReport(report: DbReport): IChartInput & { id: string } {
|
||||
return {
|
||||
id: report.id,
|
||||
events: (report.events as IChartEvent[]).map(transformEvent),
|
||||
breakdowns: report.breakdowns as IChartBreakdown[],
|
||||
chartType: report.chart_type,
|
||||
interval: report.interval,
|
||||
name: report.name || 'Untitled',
|
||||
range: (report.range as IChartRange) ?? timeRanges['1m'],
|
||||
};
|
||||
}
|
||||
|
||||
export const reportRouter = createTRPCRouter({
|
||||
get: protectedProcedure
|
||||
.input(
|
||||
@@ -72,22 +23,27 @@ export const reportRouter = createTRPCRouter({
|
||||
list: protectedProcedure
|
||||
.input(
|
||||
z.object({
|
||||
projectSlug: z.string(),
|
||||
dashboardSlug: z.string(),
|
||||
projectId: z.string(),
|
||||
dashboardId: z.string(),
|
||||
})
|
||||
)
|
||||
.query(async ({ input: { projectSlug, dashboardSlug } }) => {
|
||||
const project = await getProjectBySlug(projectSlug);
|
||||
const dashboard = await getDashboardBySlug(dashboardSlug);
|
||||
const reports = await db.report.findMany({
|
||||
where: {
|
||||
project_id: project.id,
|
||||
dashboard_id: dashboard.id,
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: 'desc',
|
||||
},
|
||||
});
|
||||
.query(async ({ input: { projectId, dashboardId } }) => {
|
||||
const [dashboard, reports] = await db.$transaction([
|
||||
db.dashboard.findUniqueOrThrow({
|
||||
where: {
|
||||
id: dashboardId,
|
||||
},
|
||||
}),
|
||||
db.report.findMany({
|
||||
where: {
|
||||
project_id: projectId,
|
||||
dashboard_id: dashboardId,
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: 'desc',
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
return {
|
||||
reports: reports.map(transformReport),
|
||||
@@ -116,6 +72,7 @@ export const reportRouter = createTRPCRouter({
|
||||
interval: report.interval,
|
||||
breakdowns: report.breakdowns,
|
||||
chart_type: report.chartType,
|
||||
line_type: report.lineType,
|
||||
range: report.range,
|
||||
},
|
||||
});
|
||||
@@ -138,6 +95,7 @@ export const reportRouter = createTRPCRouter({
|
||||
interval: report.interval,
|
||||
breakdowns: report.breakdowns,
|
||||
chart_type: report.chartType,
|
||||
line_type: report.lineType,
|
||||
range: report.range,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -56,4 +56,19 @@ export const userRouter = createTRPCRouter({
|
||||
},
|
||||
});
|
||||
}),
|
||||
invite: protectedProcedure
|
||||
.input(
|
||||
z.object({
|
||||
email: z.string().email(),
|
||||
organizationId: z.string(),
|
||||
})
|
||||
)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await db.invite.create({
|
||||
data: {
|
||||
organization_id: input.organizationId,
|
||||
email: input.email,
|
||||
},
|
||||
});
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
* need to use are documented accordingly near the end.
|
||||
*/
|
||||
|
||||
import { getServerAuthSession } from '@/server/auth';
|
||||
import { db } from '@/server/db';
|
||||
import { getSession } from '@/server/auth';
|
||||
import { initTRPC, TRPCError } from '@trpc/server';
|
||||
import type { CreateNextContextOptions } from '@trpc/server/adapters/next';
|
||||
import type { Session } from 'next-auth';
|
||||
@@ -37,10 +36,9 @@ interface CreateContextOptions {
|
||||
*
|
||||
* @see https://create.t3.gg/en/usage/trpc#-serverapitrpcts
|
||||
*/
|
||||
const createInnerTRPCContext = (opts: CreateContextOptions) => {
|
||||
export const createInnerTRPCContext = (opts: CreateContextOptions) => {
|
||||
return {
|
||||
session: opts.session,
|
||||
db,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -51,9 +49,8 @@ const createInnerTRPCContext = (opts: CreateContextOptions) => {
|
||||
* @see https://trpc.io/docs/context
|
||||
*/
|
||||
export const createTRPCContext = async (opts: CreateNextContextOptions) => {
|
||||
const { req, res } = opts;
|
||||
// Get the session from the server using the getServerSession wrapper function
|
||||
const session = await getServerAuthSession({ req, res });
|
||||
const session = await getSession();
|
||||
|
||||
return createInnerTRPCContext({
|
||||
session,
|
||||
|
||||
Reference in New Issue
Block a user