Skip to content

Commit b693b68

Browse files
Udit-takkarvolnei
andauthored
feat: booking report table backend (calcom#24794)
* feat: report table backend * fix: types * fix: types * fix: bugs * fix: type error --------- Co-authored-by: Volnei Munhoz <volnei.munhoz@gmail.com>
1 parent fb55bc2 commit b693b68

21 files changed

Lines changed: 1665 additions & 58 deletions

packages/lib/server/repository/bookingReport.interface.ts

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { BookingReportReason } from "@calcom/prisma/enums";
1+
import type { BookingReportReason, BookingReportStatus } from "@calcom/prisma/enums";
22

33
export interface CreateBookingReportInput {
44
bookingUid: string;
@@ -18,8 +18,69 @@ export interface BookingReportSummary {
1818
createdAt: Date;
1919
}
2020

21+
export interface ListBookingReportsFilters {
22+
reason?: BookingReportReason[];
23+
cancelled?: boolean;
24+
hasWatchlist?: boolean;
25+
status?: BookingReportStatus[];
26+
}
27+
28+
export interface BookingReportWithDetails {
29+
id: string;
30+
bookingUid: string;
31+
bookerEmail: string;
32+
reportedById: number | null;
33+
reason: BookingReportReason;
34+
description: string | null;
35+
cancelled: boolean;
36+
createdAt: Date;
37+
status: BookingReportStatus;
38+
watchlistId: string | null;
39+
reporter: {
40+
id: number;
41+
email: string;
42+
name: string | null;
43+
} | null;
44+
booking: {
45+
id: number;
46+
uid: string;
47+
title: string | null;
48+
startTime: Date;
49+
endTime: Date;
50+
};
51+
watchlist: {
52+
id: string;
53+
type: string;
54+
value: string;
55+
action: string;
56+
description: string | null;
57+
} | null;
58+
}
59+
2160
export interface IBookingReportRepository {
2261
createReport(input: CreateBookingReportInput): Promise<{ id: string }>;
2362

24-
findAllReportedBookings(params: { skip?: number; take?: number }): Promise<BookingReportSummary[]>;
63+
findAllReportedBookings(params: {
64+
organizationId?: number;
65+
skip?: number;
66+
take?: number;
67+
searchTerm?: string;
68+
filters?: ListBookingReportsFilters;
69+
}): Promise<{
70+
rows: BookingReportWithDetails[];
71+
meta: { totalRowCount: number };
72+
}>;
73+
74+
findReportsByIds(params: {
75+
reportIds: string[];
76+
organizationId?: number;
77+
}): Promise<BookingReportWithDetails[]>;
78+
79+
linkWatchlistToReport(params: { reportId: string; watchlistId: string }): Promise<void>;
80+
81+
updateReportStatus(params: {
82+
reportId: string;
83+
status: BookingReportStatus;
84+
organizationId?: number;
85+
}): Promise<void>;
2586
}

packages/lib/server/repository/bookingReport.test.ts

Lines changed: 89 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ describe("PrismaBookingReportRepository", () => {
1919
findUnique: vi.fn(),
2020
findMany: vi.fn(),
2121
findFirst: vi.fn(),
22+
updateMany: vi.fn(),
23+
count: vi.fn(),
2224
},
2325
} as unknown as PrismaClient;
2426

@@ -94,36 +96,112 @@ describe("PrismaBookingReportRepository", () => {
9496
const mockReports = [
9597
{
9698
id: "report-1",
99+
bookingUid: "booking-1",
100+
bookerEmail: "user1@example.com",
97101
reportedById: 1,
98102
reason: BookingReportReason.SPAM,
99103
description: "Spam",
104+
cancelled: false,
100105
createdAt: new Date("2025-01-01T10:00:00Z"),
106+
status: "PENDING",
107+
watchlistId: null,
108+
reportedBy: null,
109+
booking: {
110+
id: 1,
111+
uid: "booking-1",
112+
title: "Test Booking",
113+
startTime: new Date(),
114+
endTime: new Date(),
115+
},
116+
watchlist: null,
101117
},
102118
{
103119
id: "report-2",
120+
bookingUid: "booking-2",
121+
bookerEmail: "user2@example.com",
104122
reportedById: 2,
105123
reason: BookingReportReason.DONT_KNOW_PERSON,
106124
description: null,
125+
cancelled: false,
107126
createdAt: new Date("2025-01-01T11:00:00Z"),
127+
status: "PENDING",
128+
watchlistId: null,
129+
reportedBy: null,
130+
booking: {
131+
id: 2,
132+
uid: "booking-2",
133+
title: "Test Booking 2",
134+
startTime: new Date(),
135+
endTime: new Date(),
136+
},
137+
watchlist: null,
108138
},
109139
];
110140

111141
mockPrisma.bookingReport.findMany.mockResolvedValue(mockReports);
142+
mockPrisma.bookingReport.count.mockResolvedValue(2);
112143

113144
const result = await repository.findAllReportedBookings({ skip: 0, take: 10 });
114145

115-
expect(result).toEqual(mockReports);
116-
expect(mockPrisma.bookingReport.findMany).toHaveBeenCalledWith({
117-
skip: 0,
118-
take: 10,
119-
select: {
120-
id: true,
121-
reportedById: true,
122-
reason: true,
123-
description: true,
124-
createdAt: true,
146+
expect(result).toEqual({
147+
rows: mockReports.map((r) => ({ ...r, reporter: r.reportedBy })),
148+
meta: { totalRowCount: 2 },
149+
});
150+
expect(mockPrisma.bookingReport.findMany).toHaveBeenCalled();
151+
expect(mockPrisma.bookingReport.count).toHaveBeenCalled();
152+
});
153+
});
154+
155+
describe("updateReportStatus", () => {
156+
it("should update report status with organizationId", async () => {
157+
mockPrisma.bookingReport.updateMany.mockResolvedValue({ count: 1 });
158+
159+
await repository.updateReportStatus({
160+
reportId: "report-123",
161+
status: "BLOCKED",
162+
organizationId: 100,
163+
});
164+
165+
expect(mockPrisma.bookingReport.updateMany).toHaveBeenCalledWith({
166+
where: {
167+
id: "report-123",
168+
organizationId: 100,
169+
},
170+
data: { status: "BLOCKED" },
171+
});
172+
});
173+
174+
it("should update report status without organizationId", async () => {
175+
mockPrisma.bookingReport.updateMany.mockResolvedValue({ count: 1 });
176+
177+
await repository.updateReportStatus({
178+
reportId: "report-456",
179+
status: "DISMISSED",
180+
});
181+
182+
expect(mockPrisma.bookingReport.updateMany).toHaveBeenCalledWith({
183+
where: {
184+
id: "report-456",
185+
},
186+
data: { status: "DISMISSED" },
187+
});
188+
});
189+
190+
it("should update report status to PENDING", async () => {
191+
mockPrisma.bookingReport.updateMany.mockResolvedValue({ count: 1 });
192+
193+
await repository.updateReportStatus({
194+
reportId: "report-789",
195+
status: "PENDING",
196+
organizationId: 200,
197+
});
198+
199+
expect(mockPrisma.bookingReport.updateMany).toHaveBeenCalledWith({
200+
where: {
201+
id: "report-789",
202+
organizationId: 200,
125203
},
126-
orderBy: { createdAt: "desc" },
204+
data: { status: "PENDING" },
127205
});
128206
});
129207
});

0 commit comments

Comments
 (0)