docs: update insights chart dev guide for Prisma v6 (#23793)

This commit is contained in:
Eunjae Lee
2025-09-13 12:48:49 +00:00
committed by GitHub
parent a7fecaf578
commit 06fcea4116
2 changed files with 49 additions and 38 deletions
@@ -20,26 +20,26 @@ UI Component → tRPC Handler → Insights Service → Database Query → Respon
```typescript
// packages/features/insights/components/booking/MyNewChart.tsx
import { LineChart, XAxis, YAxis, CartesianGrid, Tooltip, Line, ResponsiveContainer } from "recharts";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc";
import { useInsightsBookingParameters } from "../../hooks/useInsightsBookingParameters";
import { ChartCard } from "../ChartCard";
import { LoadingInsight } from "../LoadingInsights";
export const MyNewChart = () => {
const { t } = useLocale();
const insightsBookingParams = useInsightsBookingParameters();
const { data, isSuccess, isPending } = trpc.viewer.insights.myNewChartData.useQuery(insightsBookingParams, {
staleTime: 180000, // 3 minutes
refetchOnWindowFocus: false,
trpc: { context: { skipBatch: true } },
});
if (isPending) return <LoadingInsight />;
return (
<ChartCard title={t("my_new_chart_title")}>
{isSuccess && data?.length > 0 ? (
@@ -66,7 +66,7 @@ UI Component → tRPC Handler → Insights Service → Database Query → Respon
<Step title="Add Component to Barrel Export">
Update the booking components index file:
```typescript
// packages/features/insights/components/booking/index.ts
export { AverageEventDurationChart } from "./AverageEventDurationChart";
@@ -76,9 +76,9 @@ UI Component → tRPC Handler → Insights Service → Database Query → Respon
```
</Step>
<Step title="Add Component to Insights View">
Add your component to the main insights page:
```typescript
// apps/web/modules/insights/insights-view.tsx
import {
@@ -86,45 +86,45 @@ UI Component → tRPC Handler → Insights Service → Database Query → Respon
BookingKPICards, // ... existing imports
MyNewChart, // Add this import
} from "@calcom/features/insights/components/booking";
export default function InsightsPage() {
// ... existing code
return (
<div className="space-y-6">
{/* Existing components */}
<BookingKPICards />
<EventTrendsChart />
{/* Add your new chart */}
<MyNewChart />
{/* Other existing components */}
</div>
);
}
```
</Step>
</Step>
<Step title="Create tRPC Handler">
Add the tRPC endpoint in the insights router using the `getInsightsBookingService()` DI container function:
```typescript
// packages/features/insights/server/trpc-router.ts
import { bookingRepositoryBaseInputSchema } from "@calcom/features/insights/server/raw-data.schema";
import { userBelongsToTeamProcedure } from "@calcom/trpc/server/procedures/authedProcedure";
import { TRPCError } from "@trpc/server";
export const insightsRouter = router({
// ... existing procedures
myNewChartData: userBelongsToTeamProcedure
.input(bookingRepositoryBaseInputSchema)
.query(async ({ ctx, input }) => {
// `createInsightsBookingService` is defined at the root level in this file
const insightsBookingService = createInsightsBookingService(ctx, input);
try {
return await insightsBookingService.getMyNewChartData();
} catch (e) {
@@ -134,24 +134,21 @@ UI Component → tRPC Handler → Insights Service → Database Query → Respon
});
```
</Step>
<Step title="Add Service Method to InsightsBookingBaseService">
<Step title="Add Service Method to InsightsBookingBaseService">
Add your new method to the `InsightsBookingBaseService` class:
```typescript
// packages/lib/server/service/InsightsBookingBaseService.ts
export class InsightsBookingBaseService {
// ... existing methods
async getMyNewChartData() {
const baseConditions = await this.getBaseConditions();
// Example: Get booking counts by day using raw SQL for performance
const data = await this.prisma.$queryRaw<
Array<{
date: Date;
bookingsCount: number;
}>
>`
// Note: Use Prisma.sql for the entire query (Prisma v6 requirement)
// Prisma v6 no longer allows mixing template literals with Prisma.sql fragments
const query = Prisma.sql`
SELECT
DATE("createdAt") as date,
COUNT(*)::int as "bookingsCount"
@@ -160,7 +157,14 @@ UI Component → tRPC Handler → Insights Service → Database Query → Respon
GROUP BY DATE("createdAt")
ORDER BY date ASC
`;
const data = await this.prisma.$queryRaw<
Array<{
date: Date;
bookingsCount: number;
}>
>(query);
// Transform the data for the chart
return data.map((item) => ({
date: item.date.toISOString().split("T")[0], // Format as YYYY-MM-DD
@@ -170,6 +174,7 @@ UI Component → tRPC Handler → Insights Service → Database Query → Respon
}
```
</Step>
</Steps>
## Best Practices
@@ -179,5 +184,6 @@ UI Component → tRPC Handler → Insights Service → Database Query → Respon
3. **Base Conditions**: Always use `await this.getBaseConditions()` for proper filtering and permissions
4. **Error Handling**: Wrap service calls in try-catch blocks with `TRPCError`
5. **Loading States**: Always show loading indicators with `LoadingInsight`
6. **Consistent Styling**: Use `recharts` for new charts.
6. **Consistent Styling**: Use `recharts` for new charts
7. **Date Handling**: Use `getDateRanges()` and `getTimeView()` for time-based charts
8. **Prisma v6 Compatibility**: Use `Prisma.sql` for the entire query instead of mixing template literals with SQL fragments
@@ -143,12 +143,9 @@ export class InsightsBookingBaseService {
const baseConditions = await this.getBaseConditions();
// Example: Get booking counts by day using raw SQL for performance
const data = await this.prisma.$queryRaw<
Array<{
date: Date;
bookingsCount: number;
}>
>`
// Note: Use Prisma.sql for the entire query (Prisma v6 requirement)
// Prisma v6 no longer allows mixing template literals with Prisma.sql fragments
const query = Prisma.sql`
SELECT
DATE("createdAt") as date,
COUNT(*)::int as "bookingsCount"
@@ -158,6 +155,13 @@ export class InsightsBookingBaseService {
ORDER BY date ASC
`;
const data = await this.prisma.$queryRaw<
Array<{
date: Date;
bookingsCount: number;
}>
>(query);
// Transform the data for the chart
return data.map((item) => ({
date: item.date.toISOString().split("T")[0], // Format as YYYY-MM-DD
@@ -174,5 +178,6 @@ export class InsightsBookingBaseService {
3. **Base Conditions**: Always use `await this.getBaseConditions()` for proper filtering and permissions
4. **Error Handling**: Wrap service calls in try-catch blocks with `TRPCError`
5. **Loading States**: Always show loading indicators with `LoadingInsight`
6. **Consistent Styling**: Use `recharts` for new charts.
6. **Consistent Styling**: Use `recharts` for new charts
7. **Date Handling**: Use `getDateRanges()` and `getTimeView()` for time-based charts
8. **Prisma v6 Compatibility**: Use `Prisma.sql` for the entire query instead of mixing template literals with SQL fragments