Compare commits
36
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fc4d0a6aa3 | ||
|
|
8e0cdf3671 | ||
|
|
fcd9480f9f | ||
|
|
09ddbe886f | ||
|
|
4ac8a51bc2 | ||
|
|
82951cd04e | ||
|
|
ae87fdcc9f | ||
|
|
35e29c4bee | ||
|
|
387a1704f3 | ||
|
|
3fb8c5bbc5 | ||
|
|
626585f5d0 | ||
|
|
4d3727db12 | ||
|
|
f6641d5173 | ||
|
|
f9b21206a2 | ||
|
|
96821c0026 | ||
|
|
3d5e4754af | ||
|
|
613e9f0788 | ||
|
|
1261247e94 | ||
|
|
2ad843ffbb | ||
|
|
6923b97cd2 | ||
|
|
e639237088 | ||
|
|
5bc1fbb8c2 | ||
|
|
e310931417 | ||
|
|
17944311c3 | ||
|
|
d82f87aa2a | ||
|
|
6aa21aceb8 | ||
|
|
870ad57a7e | ||
|
|
fddee71829 | ||
|
|
6f15a5ab0f | ||
|
|
1fd64c5d7b | ||
|
|
f8614f11ff | ||
|
|
5e5e77236a | ||
|
|
4cee53b07c | ||
|
|
c289fac008 | ||
|
|
68f9e86cdd | ||
|
|
5da754db65 |
@@ -111,7 +111,7 @@ The event types page UI components are located in `apps/web/modules/event-types/
|
||||
|
||||
Changes to shared UI patterns (like tab layouts and button alignments) need to be checked across multiple views to maintain consistency:
|
||||
- Event types page layout: `apps/web/modules/event-types/views/event-types-listing-view.tsx`
|
||||
- Bookings page layout: `apps/web/modules/bookings/views/bookings-listing-view.tsx`
|
||||
- Bookings page layout: `apps/web/modules/bookings/views/bookings-view.tsx`
|
||||
- Common elements like tabs, search bars, and filter buttons should maintain consistent alignment across views
|
||||
|
||||
## When working on workflow triggers or similar enum-based features in the Cal.com codebase
|
||||
|
||||
@@ -26,7 +26,7 @@ runs:
|
||||
with:
|
||||
path: ${{ inputs.path }}
|
||||
key: ${{ runner.os }}-${{ env.cache-name }}-${{ inputs.path }}-${{ env.key-1 }}-${{ env.key-2 }}-${{ env.key-3 }}
|
||||
- run: echo ${{ env.E2E_TEST_CALCOM_QA_GCAL_CREDENTIALS }} && yarn db-seed
|
||||
- run: USE_POOL=true yarn db-seed
|
||||
if: steps.cache-db.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
- name: Postgres Dump Backup
|
||||
|
||||
@@ -56,8 +56,8 @@ jobs:
|
||||
if: steps.cache-api-v2-build.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
export NODE_OPTIONS="--max_old_space_size=8192"
|
||||
yarn workspace @calcom/api-v2 run generate-schemas
|
||||
rm -rf apps/api/v2/node_modules
|
||||
yarn install
|
||||
yarn workspace @calcom/api-v2 run generate-schemas &&
|
||||
rm -rf apps/api/v2/node_modules &&
|
||||
yarn install &&
|
||||
yarn workspace @calcom/api-v2 run build
|
||||
shell: bash
|
||||
|
||||
@@ -16,7 +16,6 @@ env:
|
||||
CALENDSO_ENCRYPTION_KEY: ${{ secrets.CI_CALENDSO_ENCRYPTION_KEY }}
|
||||
DAILY_API_KEY: ${{ secrets.CI_DAILY_API_KEY }}
|
||||
DATABASE_URL: ${{ secrets.CI_DATABASE_URL }}
|
||||
DATABASE_DIRECT_URL: ${{ secrets.CI_DATABASE_URL }}
|
||||
E2E_TEST_APPLE_CALENDAR_EMAIL: ${{ secrets.E2E_TEST_APPLE_CALENDAR_EMAIL }}
|
||||
E2E_TEST_APPLE_CALENDAR_PASSWORD: ${{ secrets.E2E_TEST_APPLE_CALENDAR_PASSWORD }}
|
||||
E2E_TEST_MAILHOG_ENABLED: ${{ vars.E2E_TEST_MAILHOG_ENABLED }}
|
||||
|
||||
@@ -10,7 +10,6 @@ env:
|
||||
CALENDSO_ENCRYPTION_KEY: ${{ secrets.CI_CALENDSO_ENCRYPTION_KEY }}
|
||||
DAILY_API_KEY: ${{ secrets.CI_DAILY_API_KEY }}
|
||||
DATABASE_URL: ${{ secrets.CI_DATABASE_URL }}
|
||||
DATABASE_DIRECT_URL: ${{ secrets.CI_DATABASE_URL }}
|
||||
E2E_TEST_APPLE_CALENDAR_EMAIL: ${{ secrets.E2E_TEST_APPLE_CALENDAR_EMAIL }}
|
||||
E2E_TEST_APPLE_CALENDAR_PASSWORD: ${{ secrets.E2E_TEST_APPLE_CALENDAR_PASSWORD }}
|
||||
GOOGLE_API_CREDENTIALS: ${{ secrets.CI_GOOGLE_API_CREDENTIALS }}
|
||||
@@ -36,7 +35,6 @@ env:
|
||||
NEXT_PUBLIC_API_V2_ROOT_URL: ${{ secrets.CI_NEXT_PUBLIC_API_V2_ROOT_URL }}
|
||||
NEXT_PUBLIC_ORG_SELF_SERVE_ENABLED: ${{ vars.CI_NEXT_PUBLIC_ORG_SELF_SERVE_ENABLED }}
|
||||
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build Web App
|
||||
|
||||
@@ -67,6 +67,9 @@ tsconfig.tsbuildinfo
|
||||
|
||||
# Prisma generated files
|
||||
packages/prisma/client/*
|
||||
!packages/prisma/client/index.ts
|
||||
apps/api/v2/generated/prisma/*
|
||||
packages/prisma/generated/prisma/*
|
||||
packages/prisma/zod/**/*.ts
|
||||
packages/prisma/enums
|
||||
|
||||
|
||||
Vendored
Vendored
Vendored
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
undecided:
|
||||
- calcom-monorepo
|
||||
@@ -234,9 +234,10 @@ for Logger level to be set at info, for example.
|
||||
|
||||
</details>
|
||||
|
||||
If you don't want to create a local DB. Then you can also consider using services like railway.app or render.
|
||||
If you don't want to create a local DB. Then you can also consider using services like railway.app, Northflank or render.
|
||||
|
||||
- [Setup postgres DB with railway.app](https://docs.railway.app/guides/postgresql)
|
||||
- [Setup postgres DB with Northflank](https://northflank.com/guides/deploy-postgres-database-on-northflank)
|
||||
- [Setup postgres DB with render](https://render.com/docs/databases)
|
||||
|
||||
1. Copy and paste your `DATABASE_URL` from `.env` to `.env.appStore`.
|
||||
@@ -390,6 +391,12 @@ Cal.com, Inc. does not provide official support for Docker, but we will accept f
|
||||
|
||||
You can deploy Cal.com on [Railway](https://railway.app) using the button above. The team at Railway also have a [detailed blog post](https://blog.railway.app/p/calendso) on deploying Cal.com on their platform.
|
||||
|
||||
### Northflank
|
||||
|
||||
[](https://northflank.com/stacks/deploy-calcom)
|
||||
|
||||
You can deploy Cal.com on [Northflank](https://northflank.com) using the button above. The team at Northflank also have a [detailed blog post](https://northflank.com/guides/deploy-calcom-with-northflank) on deploying Cal.com on their platform.
|
||||
|
||||
### Vercel
|
||||
|
||||
Currently Vercel Pro Plan is required to be able to Deploy this application with Vercel, due to limitations on the number of serverless functions on the free plan.
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
import { get } from "@vercel/edge-config";
|
||||
import type { NextMiddleware } from "next-api-middleware";
|
||||
|
||||
const safeGet = async <T = unknown>(key: string): Promise<T | undefined> => {
|
||||
try {
|
||||
return get<T>(key);
|
||||
} catch (error) {
|
||||
// Don't crash if EDGE_CONFIG env var is missing
|
||||
}
|
||||
};
|
||||
|
||||
export const config = { matcher: "/:path*" };
|
||||
|
||||
export const checkIsInMaintenanceMode: NextMiddleware = async (req, res, next) => {
|
||||
const isInMaintenanceMode = await safeGet<boolean>("isInMaintenanceMode");
|
||||
if (isInMaintenanceMode) {
|
||||
return res
|
||||
.status(503)
|
||||
.json({ message: "API is currently under maintenance. Please try again at a later time." });
|
||||
}
|
||||
|
||||
await next();
|
||||
};
|
||||
@@ -3,7 +3,6 @@ import { label } from "next-api-middleware";
|
||||
import { addRequestId } from "./addRequestid";
|
||||
import { captureErrors } from "./captureErrors";
|
||||
import { captureUserId } from "./captureUserId";
|
||||
import { checkIsInMaintenanceMode } from "./checkIsInMaintenanceMode";
|
||||
import { extendRequest } from "./extendRequest";
|
||||
import {
|
||||
HTTP_DELETE,
|
||||
@@ -26,7 +25,6 @@ const middleware = {
|
||||
HTTP_POST,
|
||||
HTTP_DELETE,
|
||||
addRequestId,
|
||||
checkIsInMaintenanceMode,
|
||||
verifyApiKey,
|
||||
rateLimitApiKey,
|
||||
extendRequest,
|
||||
@@ -38,16 +36,14 @@ const middleware = {
|
||||
|
||||
type Middleware = keyof typeof middleware;
|
||||
|
||||
const middlewareOrder = [
|
||||
// The order here, determines the order of execution
|
||||
"checkIsInMaintenanceMode",
|
||||
const middlewareOrder: Middleware[] = [
|
||||
"extendRequest",
|
||||
"captureErrors",
|
||||
"verifyApiKey",
|
||||
"rateLimitApiKey",
|
||||
"addRequestId",
|
||||
"captureUserId",
|
||||
] as Middleware[]; // <-- Provide a list of middleware to call automatically
|
||||
];
|
||||
|
||||
const withMiddleware = label(middleware, middlewareOrder);
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ const getAllAdminMemberships = async (userId: number) => {
|
||||
where: {
|
||||
userId: userId,
|
||||
accepted: true,
|
||||
OR: [{ role: MembershipRole.OWNER }, { role: MembershipRole.ADMIN }],
|
||||
role: { in: [MembershipRole.OWNER, MembershipRole.ADMIN] },
|
||||
},
|
||||
select: {
|
||||
team: {
|
||||
@@ -56,16 +56,19 @@ export const getAccessibleUsers = async ({
|
||||
memberUserIds,
|
||||
adminUserId,
|
||||
}: AccessibleUsersType): Promise<number[]> => {
|
||||
const orConditions = [];
|
||||
if (memberUserIds.length > 0) {
|
||||
orConditions.push({ userId: { in: memberUserIds } });
|
||||
}
|
||||
orConditions.push({ userId: adminUserId, role: { in: [MembershipRole.OWNER, MembershipRole.ADMIN] } });
|
||||
|
||||
const memberships = await prisma.membership.findMany({
|
||||
where: {
|
||||
team: {
|
||||
isOrganization: true,
|
||||
},
|
||||
accepted: true,
|
||||
OR: [
|
||||
{ userId: { in: memberUserIds } },
|
||||
{ userId: adminUserId, role: { in: [MembershipRole.OWNER, MembershipRole.ADMIN] } },
|
||||
],
|
||||
OR: orConditions,
|
||||
},
|
||||
select: {
|
||||
userId: true,
|
||||
|
||||
@@ -12,7 +12,7 @@ async function authMiddleware(req: NextApiRequest) {
|
||||
if (isSystemWideAdmin) return;
|
||||
// Find all references where the user has bookings
|
||||
const bookingReference = await prisma.bookingReference.findFirst({
|
||||
where: { id, booking: { userId } },
|
||||
where: { id, booking: { userId }, deleted: null },
|
||||
});
|
||||
if (!bookingReference) throw new HttpError({ statusCode: 403, message: "Forbidden" });
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ import { schemaQueryIdParseInt } from "~/lib/validations/shared/queryIdTransform
|
||||
export async function deleteHandler(req: NextApiRequest) {
|
||||
const { query } = req;
|
||||
const { id } = schemaQueryIdParseInt.parse(query);
|
||||
await prisma.bookingReference.delete({ where: { id } });
|
||||
await prisma.bookingReference.update({ where: { id }, data: { deleted: true } });
|
||||
return { message: `BookingReference with id: ${id} deleted` };
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ import { schemaQueryIdParseInt } from "~/lib/validations/shared/queryIdTransform
|
||||
export async function getHandler(req: NextApiRequest) {
|
||||
const { query } = req;
|
||||
const { id } = schemaQueryIdParseInt.parse(query);
|
||||
const booking_reference = await prisma.bookingReference.findUniqueOrThrow({ where: { id } });
|
||||
const booking_reference = await prisma.bookingReference.findUniqueOrThrow({ where: { id, deleted: null } });
|
||||
return { booking_reference: schemaBookingReferenceReadPublic.parse(booking_reference) };
|
||||
}
|
||||
|
||||
|
||||
@@ -29,13 +29,13 @@ import { schemaBookingReferenceReadPublic } from "~/lib/validations/booking-refe
|
||||
* 404:
|
||||
* description: No booking references were found
|
||||
*/
|
||||
async function getHandler(req: NextApiRequest) {
|
||||
export async function handler(req: NextApiRequest) {
|
||||
const { userId, isSystemWideAdmin } = req;
|
||||
const args: Prisma.BookingReferenceFindManyArgs = isSystemWideAdmin
|
||||
? {}
|
||||
: { where: { booking: { userId } } };
|
||||
? { where: { deleted: null } }
|
||||
: { where: { booking: { userId }, deleted: null } };
|
||||
const data = await prisma.bookingReference.findMany(args);
|
||||
return { booking_references: data.map((br) => schemaBookingReferenceReadPublic.parse(br)) };
|
||||
}
|
||||
|
||||
export default defaultResponder(getHandler);
|
||||
export default defaultResponder(handler);
|
||||
|
||||
@@ -3,8 +3,8 @@ import utc from "dayjs/plugin/utc";
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
|
||||
import dayjs from "@calcom/dayjs";
|
||||
import { isSupportedTimeZone } from "@calcom/lib/dayjs";
|
||||
import { getAvailableSlotsService } from "@calcom/features/di/containers/AvailableSlots";
|
||||
import { isSupportedTimeZone } from "@calcom/lib/dayjs";
|
||||
import { HttpError } from "@calcom/lib/http-error";
|
||||
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
|
||||
import { createContext } from "@calcom/trpc/server/createContext";
|
||||
@@ -48,7 +48,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
: availableSlots.slots;
|
||||
|
||||
return { slots: slotsInProvidedTimeZone };
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
||||
} catch (cause) {
|
||||
if (cause instanceof TRPCError) {
|
||||
const statusCode = getHTTPStatusCodeFromError(cause);
|
||||
|
||||
@@ -0,0 +1,486 @@
|
||||
import type { Request, Response } from "express";
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
import { createMocks } from "node-mocks-http";
|
||||
import { describe, it, expect, beforeAll, afterAll } from "vitest";
|
||||
|
||||
import prisma from "@calcom/prisma";
|
||||
import type { Booking, Credential, EventType, User } from "@calcom/prisma/client";
|
||||
import { BookingStatus } from "@calcom/prisma/enums";
|
||||
|
||||
import { patchHandler } from "../../pages/api/booking-references/[id]/_patch";
|
||||
import { handler } from "../../pages/api/booking-references/_get";
|
||||
|
||||
type CustomNextApiRequest = NextApiRequest & Request;
|
||||
type CustomNextApiResponse = NextApiResponse & Response;
|
||||
|
||||
describe("GET /api/booking-references - Soft-delete filtering", () => {
|
||||
let testUser: User;
|
||||
let testCredential: Credential;
|
||||
let testEventType: EventType;
|
||||
let testBooking: Booking;
|
||||
const createdBookingReferenceIds: number[] = [];
|
||||
|
||||
beforeAll(async () => {
|
||||
testUser = await prisma.user.create({
|
||||
data: {
|
||||
email: "api-bookingreference-test@example.com",
|
||||
username: "api-bookingreference-test",
|
||||
},
|
||||
});
|
||||
|
||||
testCredential = await prisma.credential.create({
|
||||
data: {
|
||||
type: "google_calendar",
|
||||
key: {},
|
||||
userId: testUser.id,
|
||||
},
|
||||
});
|
||||
|
||||
testEventType = await prisma.eventType.create({
|
||||
data: {
|
||||
title: "Test Event Type",
|
||||
slug: "api-test-event-type",
|
||||
length: 30,
|
||||
userId: testUser.id,
|
||||
},
|
||||
});
|
||||
|
||||
testBooking = await prisma.booking.create({
|
||||
data: {
|
||||
uid: "api-test-booking-uid",
|
||||
title: "API Test Booking",
|
||||
startTime: new Date(Date.now() + 86400000),
|
||||
endTime: new Date(Date.now() + 90000000),
|
||||
userId: testUser.id,
|
||||
eventTypeId: testEventType.id,
|
||||
status: BookingStatus.ACCEPTED,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
if (createdBookingReferenceIds.length > 0) {
|
||||
await prisma.bookingReference.deleteMany({
|
||||
where: {
|
||||
id: {
|
||||
in: createdBookingReferenceIds,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
await prisma.booking.delete({
|
||||
where: {
|
||||
id: testBooking.id,
|
||||
},
|
||||
});
|
||||
|
||||
await prisma.eventType.delete({
|
||||
where: {
|
||||
id: testEventType.id,
|
||||
},
|
||||
});
|
||||
|
||||
await prisma.credential.delete({
|
||||
where: {
|
||||
id: testCredential.id,
|
||||
},
|
||||
});
|
||||
|
||||
await prisma.user.delete({
|
||||
where: {
|
||||
id: testUser.id,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("should only return active booking references, excluding soft-deleted ones", async () => {
|
||||
const timestamp = Date.now();
|
||||
const activeRef = await prisma.bookingReference.create({
|
||||
data: {
|
||||
type: "google_calendar",
|
||||
uid: `api-active-ref-${timestamp}`,
|
||||
meetingId: `api-active-meeting-${timestamp}`,
|
||||
credentialId: testCredential.id,
|
||||
bookingId: testBooking.id,
|
||||
},
|
||||
});
|
||||
createdBookingReferenceIds.push(activeRef.id);
|
||||
|
||||
const deletedRef = await prisma.bookingReference.create({
|
||||
data: {
|
||||
type: "google_calendar",
|
||||
uid: `api-deleted-ref-${timestamp}`,
|
||||
meetingId: `api-deleted-meeting-${timestamp}`,
|
||||
credentialId: testCredential.id,
|
||||
bookingId: testBooking.id,
|
||||
deleted: true,
|
||||
},
|
||||
});
|
||||
createdBookingReferenceIds.push(deletedRef.id);
|
||||
|
||||
const { req } = createMocks<CustomNextApiRequest, CustomNextApiResponse>({
|
||||
method: "GET",
|
||||
});
|
||||
|
||||
req.userId = testUser.id;
|
||||
|
||||
const responseData = await handler(req);
|
||||
|
||||
expect(responseData.booking_references).toBeDefined();
|
||||
expect(Array.isArray(responseData.booking_references)).toBe(true);
|
||||
|
||||
const returnedActiveRef = responseData.booking_references.find(
|
||||
(ref: { id: number }) => ref.id === activeRef.id
|
||||
);
|
||||
const returnedDeletedRef = responseData.booking_references.find(
|
||||
(ref: { id: number }) => ref.id === deletedRef.id
|
||||
);
|
||||
|
||||
expect(returnedActiveRef).toBeDefined();
|
||||
expect(returnedDeletedRef).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should filter booking references by user when not system admin", async () => {
|
||||
const timestamp = Date.now();
|
||||
const otherUser = await prisma.user.create({
|
||||
data: {
|
||||
email: `other-user-bookingreference-${timestamp}@example.com`,
|
||||
username: `other-user-bookingreference-${timestamp}`,
|
||||
},
|
||||
});
|
||||
|
||||
const otherBooking = await prisma.booking.create({
|
||||
data: {
|
||||
uid: `other-user-booking-uid-${timestamp}`,
|
||||
title: "Other User Booking",
|
||||
startTime: new Date(Date.now() + 86400000),
|
||||
endTime: new Date(Date.now() + 90000000),
|
||||
userId: otherUser.id,
|
||||
eventTypeId: testEventType.id,
|
||||
status: BookingStatus.ACCEPTED,
|
||||
},
|
||||
});
|
||||
|
||||
const otherUserRef = await prisma.bookingReference.create({
|
||||
data: {
|
||||
type: "zoom_video",
|
||||
uid: `other-user-ref-${timestamp}`,
|
||||
meetingId: `other-user-meeting-${timestamp}`,
|
||||
credentialId: testCredential.id,
|
||||
bookingId: otherBooking.id,
|
||||
},
|
||||
});
|
||||
createdBookingReferenceIds.push(otherUserRef.id);
|
||||
|
||||
const { req } = createMocks<CustomNextApiRequest, CustomNextApiResponse>({
|
||||
method: "GET",
|
||||
});
|
||||
|
||||
req.userId = testUser.id;
|
||||
|
||||
const responseData = await handler(req);
|
||||
|
||||
const userRefs = responseData.booking_references.filter(
|
||||
(ref: { id: number }) => ref.id === otherUserRef.id
|
||||
);
|
||||
|
||||
expect(userRefs).toHaveLength(0);
|
||||
|
||||
await prisma.booking.delete({ where: { id: otherBooking.id } });
|
||||
await prisma.user.delete({ where: { id: otherUser.id } });
|
||||
});
|
||||
|
||||
it("should return only active booking references for user, not soft-deleted ones for system admin", async () => {
|
||||
const timestamp = Date.now();
|
||||
const activeRef1 = await prisma.bookingReference.create({
|
||||
data: {
|
||||
type: "daily_video",
|
||||
uid: `admin-active-ref-1-${timestamp}`,
|
||||
meetingId: `admin-active-meeting-1-${timestamp}`,
|
||||
credentialId: testCredential.id,
|
||||
bookingId: testBooking.id,
|
||||
},
|
||||
});
|
||||
createdBookingReferenceIds.push(activeRef1.id);
|
||||
|
||||
const activeRef2 = await prisma.bookingReference.create({
|
||||
data: {
|
||||
type: "daily_video",
|
||||
uid: `admin-active-ref-2-${timestamp}`,
|
||||
meetingId: `admin-active-meeting-2-${timestamp}`,
|
||||
credentialId: testCredential.id,
|
||||
bookingId: testBooking.id,
|
||||
},
|
||||
});
|
||||
createdBookingReferenceIds.push(activeRef2.id);
|
||||
|
||||
const deletedRef = await prisma.bookingReference.create({
|
||||
data: {
|
||||
type: "daily_video",
|
||||
uid: `admin-deleted-ref-${timestamp}`,
|
||||
meetingId: `admin-deleted-meeting-${timestamp}`,
|
||||
credentialId: testCredential.id,
|
||||
bookingId: testBooking.id,
|
||||
deleted: true,
|
||||
},
|
||||
});
|
||||
createdBookingReferenceIds.push(deletedRef.id);
|
||||
|
||||
const { req } = createMocks<CustomNextApiRequest, CustomNextApiResponse>({
|
||||
method: "GET",
|
||||
});
|
||||
|
||||
req.userId = testUser.id;
|
||||
|
||||
const responseData = await handler(req);
|
||||
|
||||
const activeRefs = responseData.booking_references.filter(
|
||||
(ref: { id: number }) => ref.id === activeRef1.id || ref.id === activeRef2.id
|
||||
);
|
||||
const returnedDeletedRef = responseData.booking_references.find(
|
||||
(ref: { id: number }) => ref.id === deletedRef.id
|
||||
);
|
||||
|
||||
expect(activeRefs.length).toBe(2);
|
||||
expect(returnedDeletedRef).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should verify soft-deleted references are never returned through the API", async () => {
|
||||
const timestamp = Date.now();
|
||||
const activeRef = await prisma.bookingReference.create({
|
||||
data: {
|
||||
type: "office365_calendar",
|
||||
uid: `never-return-active-${timestamp}`,
|
||||
meetingId: `never-return-active-meeting-${timestamp}`,
|
||||
credentialId: testCredential.id,
|
||||
bookingId: testBooking.id,
|
||||
},
|
||||
});
|
||||
createdBookingReferenceIds.push(activeRef.id);
|
||||
|
||||
await prisma.bookingReference.update({
|
||||
where: { id: activeRef.id },
|
||||
data: { deleted: true },
|
||||
});
|
||||
|
||||
const { req } = createMocks<CustomNextApiRequest, CustomNextApiResponse>({
|
||||
method: "GET",
|
||||
});
|
||||
|
||||
req.userId = testUser.id;
|
||||
|
||||
const responseData = await handler(req);
|
||||
|
||||
const softDeletedRef = responseData.booking_references.find(
|
||||
(ref: { id: number }) => ref.id === activeRef.id
|
||||
);
|
||||
|
||||
expect(softDeletedRef).toBeUndefined();
|
||||
|
||||
const refInDb = await prisma.bookingReference.findUnique({
|
||||
where: { id: activeRef.id },
|
||||
});
|
||||
expect(refInDb).toBeDefined();
|
||||
expect(refInDb?.deleted).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("PATCH /api/booking-references/[id] - Existing functionality", () => {
|
||||
let testUser: User;
|
||||
let testCredential: Credential;
|
||||
let testEventType: EventType;
|
||||
let testBooking: Booking;
|
||||
const createdBookingReferenceIds: number[] = [];
|
||||
|
||||
beforeAll(async () => {
|
||||
testUser = await prisma.user.create({
|
||||
data: {
|
||||
email: "api-bookingreference-patch-test@example.com",
|
||||
username: "api-bookingreference-patch-test",
|
||||
},
|
||||
});
|
||||
|
||||
testCredential = await prisma.credential.create({
|
||||
data: {
|
||||
type: "google_calendar",
|
||||
key: {},
|
||||
userId: testUser.id,
|
||||
},
|
||||
});
|
||||
|
||||
testEventType = await prisma.eventType.create({
|
||||
data: {
|
||||
title: "Test Event Type",
|
||||
slug: "api-patch-test-event-type",
|
||||
length: 30,
|
||||
userId: testUser.id,
|
||||
},
|
||||
});
|
||||
|
||||
testBooking = await prisma.booking.create({
|
||||
data: {
|
||||
uid: "api-patch-test-booking-uid",
|
||||
title: "API Patch Test Booking",
|
||||
startTime: new Date(Date.now() + 86400000),
|
||||
endTime: new Date(Date.now() + 90000000),
|
||||
userId: testUser.id,
|
||||
eventTypeId: testEventType.id,
|
||||
status: BookingStatus.ACCEPTED,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
if (createdBookingReferenceIds.length > 0) {
|
||||
await prisma.bookingReference.deleteMany({
|
||||
where: {
|
||||
id: {
|
||||
in: createdBookingReferenceIds,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
await prisma.booking.delete({
|
||||
where: {
|
||||
id: testBooking.id,
|
||||
},
|
||||
});
|
||||
|
||||
await prisma.eventType.delete({
|
||||
where: {
|
||||
id: testEventType.id,
|
||||
},
|
||||
});
|
||||
|
||||
await prisma.credential.delete({
|
||||
where: {
|
||||
id: testCredential.id,
|
||||
},
|
||||
});
|
||||
|
||||
await prisma.user.delete({
|
||||
where: {
|
||||
id: testUser.id,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("should successfully update an active booking reference", async () => {
|
||||
const timestamp = Date.now();
|
||||
const activeRef = await prisma.bookingReference.create({
|
||||
data: {
|
||||
type: "google_calendar",
|
||||
uid: `patch-active-ref-${timestamp}`,
|
||||
meetingId: `patch-active-meeting-${timestamp}`,
|
||||
credentialId: testCredential.id,
|
||||
bookingId: testBooking.id,
|
||||
},
|
||||
});
|
||||
createdBookingReferenceIds.push(activeRef.id);
|
||||
|
||||
const { req } = createMocks<CustomNextApiRequest, CustomNextApiResponse>({
|
||||
method: "PATCH",
|
||||
query: { id: activeRef.id.toString() },
|
||||
body: {
|
||||
meetingPassword: "new-password-123",
|
||||
},
|
||||
});
|
||||
|
||||
req.userId = testUser.id;
|
||||
req.isSystemWideAdmin = false;
|
||||
|
||||
const responseData = await patchHandler(req);
|
||||
|
||||
expect(responseData.booking_reference).toBeDefined();
|
||||
expect(responseData.booking_reference.id).toBe(activeRef.id);
|
||||
expect(responseData.booking_reference.meetingPassword).toBe("new-password-123");
|
||||
|
||||
const updatedRef = await prisma.bookingReference.findUnique({
|
||||
where: { id: activeRef.id },
|
||||
});
|
||||
expect(updatedRef?.meetingPassword).toBe("new-password-123");
|
||||
});
|
||||
|
||||
it("should allow updating a soft-deleted booking reference (existing functionality)", async () => {
|
||||
const timestamp = Date.now();
|
||||
const deletedRef = await prisma.bookingReference.create({
|
||||
data: {
|
||||
type: "google_calendar",
|
||||
uid: `patch-deleted-ref-${timestamp}`,
|
||||
meetingId: `patch-deleted-meeting-${timestamp}`,
|
||||
credentialId: testCredential.id,
|
||||
bookingId: testBooking.id,
|
||||
deleted: true,
|
||||
meetingPassword: "original-password",
|
||||
},
|
||||
});
|
||||
createdBookingReferenceIds.push(deletedRef.id);
|
||||
|
||||
const { req } = createMocks<CustomNextApiRequest, CustomNextApiResponse>({
|
||||
method: "PATCH",
|
||||
query: { id: deletedRef.id.toString() },
|
||||
body: {
|
||||
meetingPassword: "updated-password",
|
||||
},
|
||||
});
|
||||
|
||||
req.userId = testUser.id;
|
||||
req.isSystemWideAdmin = false;
|
||||
|
||||
const responseData = await patchHandler(req);
|
||||
|
||||
expect(responseData.booking_reference).toBeDefined();
|
||||
expect(responseData.booking_reference.id).toBe(deletedRef.id);
|
||||
expect(responseData.booking_reference.meetingPassword).toBe("updated-password");
|
||||
|
||||
const updatedRef = await prisma.bookingReference.findUnique({
|
||||
where: { id: deletedRef.id },
|
||||
});
|
||||
expect(updatedRef?.meetingPassword).toBe("updated-password");
|
||||
expect(updatedRef?.deleted).toBe(true);
|
||||
});
|
||||
|
||||
it("should verify that booking references can be updated regardless of deleted status", async () => {
|
||||
const timestamp = Date.now();
|
||||
const activeRef = await prisma.bookingReference.create({
|
||||
data: {
|
||||
type: "zoom_video",
|
||||
uid: `patch-verify-active-${timestamp}`,
|
||||
meetingId: `patch-verify-active-meeting-${timestamp}`,
|
||||
credentialId: testCredential.id,
|
||||
bookingId: testBooking.id,
|
||||
meetingPassword: "original-password",
|
||||
},
|
||||
});
|
||||
createdBookingReferenceIds.push(activeRef.id);
|
||||
|
||||
await prisma.bookingReference.update({
|
||||
where: { id: activeRef.id },
|
||||
data: { deleted: true },
|
||||
});
|
||||
|
||||
const { req } = createMocks<CustomNextApiRequest, CustomNextApiResponse>({
|
||||
method: "PATCH",
|
||||
query: { id: activeRef.id.toString() },
|
||||
body: {
|
||||
meetingPassword: "updated-password",
|
||||
},
|
||||
});
|
||||
|
||||
req.userId = testUser.id;
|
||||
req.isSystemWideAdmin = false;
|
||||
|
||||
const responseData = await patchHandler(req);
|
||||
|
||||
expect(responseData.booking_reference).toBeDefined();
|
||||
expect(responseData.booking_reference.meetingPassword).toBe("updated-password");
|
||||
|
||||
const refInDb = await prisma.bookingReference.findUnique({
|
||||
where: { id: activeRef.id },
|
||||
});
|
||||
expect(refInDb?.meetingPassword).toBe("updated-password");
|
||||
expect(refInDb?.deleted).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -5,11 +5,11 @@ import type { NextApiRequest, NextApiResponse } from "next";
|
||||
import { createMocks } from "node-mocks-http";
|
||||
import { describe, expect, test, vi, afterEach } from "vitest";
|
||||
|
||||
import { buildBooking } from "@calcom/lib/test/builder";
|
||||
import {
|
||||
getRecordingsOfCalVideoByRoomName,
|
||||
getDownloadLinkOfCalVideoByRecordingId,
|
||||
} from "@calcom/features/conferencing/lib/videoClient";
|
||||
import { buildBooking } from "@calcom/lib/test/builder";
|
||||
|
||||
import { getAccessibleUsers } from "~/lib/utils/retrieveScopedAccessibleUsers";
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@ import type { NextApiRequest, NextApiResponse } from "next";
|
||||
import { createMocks } from "node-mocks-http";
|
||||
import { describe, expect, test, vi, afterEach } from "vitest";
|
||||
|
||||
import { buildBooking } from "@calcom/lib/test/builder";
|
||||
import { getAllTranscriptsAccessLinkFromRoomName } from "@calcom/features/conferencing/lib/videoClient";
|
||||
import { buildBooking } from "@calcom/lib/test/builder";
|
||||
|
||||
import { getAccessibleUsers } from "~/lib/utils/retrieveScopedAccessibleUsers";
|
||||
|
||||
|
||||
@@ -42,4 +42,5 @@ lerna-debug.log*
|
||||
.env.*
|
||||
!.env.example
|
||||
!.env.appStore.example
|
||||
/client/*
|
||||
/client/*
|
||||
/generated/prisma/*
|
||||
|
||||
@@ -22,6 +22,10 @@ const loadConfig = (): AppConfig => {
|
||||
db: {
|
||||
readUrl: getEnv("DATABASE_READ_URL"),
|
||||
writeUrl: getEnv("DATABASE_WRITE_URL"),
|
||||
readPoolMax: getEnv("DATABASE_READ_POOL_MAX", 9),
|
||||
writePoolMax: getEnv("DATABASE_WRITE_POOL_MAX", 7),
|
||||
workerReadPoolMax: getEnv("DATABASE_READ_WORKER_POOL_MAX", 4),
|
||||
workerWritePoolMax: getEnv("DATABASE_WRITE_WORKER_POOL_MAX", 6),
|
||||
redisUrl: getEnv("REDIS_URL"),
|
||||
},
|
||||
next: {
|
||||
|
||||
@@ -13,6 +13,10 @@ export type AppConfig = {
|
||||
db: {
|
||||
readUrl: string;
|
||||
writeUrl: string;
|
||||
readPoolMax: number;
|
||||
writePoolMax: number;
|
||||
workerReadPoolMax: number;
|
||||
workerWritePoolMax: number;
|
||||
redisUrl: string;
|
||||
};
|
||||
next: {
|
||||
|
||||
@@ -9,7 +9,7 @@ export class BookingReferencesRepository_2024_08_13 {
|
||||
constructor(private readonly dbRead: PrismaReadService) {}
|
||||
|
||||
async getBookingReferences(bookingId: number, filter?: BookingReferencesFilterInput_2024_08_13) {
|
||||
const whereClause: Prisma.BookingReferenceWhereInput = { bookingId };
|
||||
const whereClause: Prisma.BookingReferenceWhereInput = { bookingId, deleted: null };
|
||||
|
||||
if (filter?.type) {
|
||||
whereClause.type = filter.type;
|
||||
@@ -30,6 +30,7 @@ export class BookingReferencesRepository_2024_08_13 {
|
||||
return this.dbRead.prisma.bookingReference.findFirst({
|
||||
where: {
|
||||
uid: eventUid,
|
||||
deleted: null,
|
||||
},
|
||||
include: {
|
||||
credential: true,
|
||||
|
||||
+3
-1
@@ -39,9 +39,11 @@ export class EventTypesService_2024_06_14 {
|
||||
}
|
||||
await this.checkCanCreateEventType(user.id, body);
|
||||
const eventTypeUser = await this.getUserToCreateEvent(user);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
|
||||
const { destinationCalendar: _destinationCalendar, ...rest } = body;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
const { eventType: eventTypeCreated } = await createEventType({
|
||||
input: rest,
|
||||
ctx: {
|
||||
|
||||
@@ -34,6 +34,10 @@ export type Environment = {
|
||||
IS_TEAM_BILLING_ENABLED: boolean;
|
||||
// Used to enable/disable the rewrite of /api/v2 to /v2, active by default.
|
||||
REWRITE_API_V2_PREFIX: string;
|
||||
DATABASE_READ_POOL_MAX: number;
|
||||
DATABASE_WRITE_POOL_MAX: number;
|
||||
DATABASE_READ_WORKER_POOL_MAX: number;
|
||||
DATABASE_WRITE_WORKER_POOL_MAX: number;
|
||||
};
|
||||
|
||||
export const getEnv = <K extends keyof Environment>(key: K, fallback?: Environment[K]): Environment[K] => {
|
||||
|
||||
@@ -16,12 +16,12 @@ import { FilterHostsService } from "@/lib/services/filter-hosts.service";
|
||||
import { NoSlotsNotificationService } from "@/lib/services/no-slots-notification.service";
|
||||
import { QualifiedHostsService } from "@/lib/services/qualified-hosts.service";
|
||||
import { UserAvailabilityService } from "@/lib/services/user-availability.service";
|
||||
import { PrismaModule } from "@/modules/prisma/prisma.module";
|
||||
import { PrismaWorkerModule } from "@/modules/prisma/prisma-worker.module";
|
||||
import { RedisService } from "@/modules/redis/redis.service";
|
||||
import { Module } from "@nestjs/common";
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule],
|
||||
imports: [PrismaWorkerModule],
|
||||
providers: [
|
||||
PrismaOOORepository,
|
||||
PrismaScheduleRepository,
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import { BookingCancelService } from "@/lib/services/booking-cancel.service";
|
||||
import { PrismaModule } from "@/modules/prisma/prisma.module";
|
||||
import { Module } from "@nestjs/common";
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule],
|
||||
providers: [BookingCancelService],
|
||||
exports: [BookingCancelService],
|
||||
})
|
||||
export class BookingCancelModule {}
|
||||
@@ -7,6 +7,7 @@ import { PrismaUserRepository } from "@/lib/repositories/prisma-user.repository"
|
||||
import { CacheService } from "@/lib/services/cache.service";
|
||||
import { CheckBookingAndDurationLimitsService } from "@/lib/services/check-booking-and-duration-limits.service";
|
||||
import { CheckBookingLimitsService } from "@/lib/services/check-booking-limits.service";
|
||||
import { HashedLinkService } from "@/lib/services/hashed-link.service";
|
||||
import { LuckyUserService } from "@/lib/services/lucky-user.service";
|
||||
import { RegularBookingService } from "@/lib/services/regular-booking.service";
|
||||
import { PrismaModule } from "@/modules/prisma/prisma.module";
|
||||
@@ -24,9 +25,10 @@ import { Module } from "@nestjs/common";
|
||||
CacheService,
|
||||
CheckBookingAndDurationLimitsService,
|
||||
CheckBookingLimitsService,
|
||||
HashedLinkService,
|
||||
LuckyUserService,
|
||||
RegularBookingService,
|
||||
],
|
||||
exports: [RegularBookingService],
|
||||
})
|
||||
export class RegularBookingModule {}
|
||||
export class RegularBookingModule { }
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
|
||||
import { Injectable } from "@nestjs/common";
|
||||
|
||||
import { BookingCancelService as BaseBookingCancelService } from "@calcom/features/bookings/lib/handleCancelBooking";
|
||||
|
||||
@Injectable()
|
||||
export class BookingCancelService extends BaseBookingCancelService {
|
||||
constructor(prismaWriteService: PrismaWriteService) {
|
||||
super({
|
||||
prismaClient: prismaWriteService.prisma,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { Injectable } from "@nestjs/common";
|
||||
|
||||
import { HashedLinkService as BaseHashedLinkService } from "@calcom/platform-libraries/private-links";
|
||||
|
||||
@Injectable()
|
||||
export class HashedLinkService extends BaseHashedLinkService {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,8 @@
|
||||
import { PrismaAttributeRepository } from "@/lib/repositories/prisma-attribute.repository";
|
||||
import { PrismaBookingRepository } from "@/lib/repositories/prisma-booking.repository";
|
||||
import { PrismaFeaturesRepository } from "@/lib/repositories/prisma-features.repository";
|
||||
import { PrismaHostRepository } from "@/lib/repositories/prisma-host.repository";
|
||||
import { PrismaOOORepository } from "@/lib/repositories/prisma-ooo.repository";
|
||||
import { PrismaUserRepository } from "@/lib/repositories/prisma-user.repository";
|
||||
import { CacheService } from "@/lib/services/cache.service";
|
||||
import { CheckBookingAndDurationLimitsService } from "@/lib/services/check-booking-and-duration-limits.service";
|
||||
import { CheckBookingLimitsService } from "@/lib/services/check-booking-limits.service";
|
||||
import { HashedLinkService } from "@/lib/services/hashed-link.service";
|
||||
import { LuckyUserService } from "@/lib/services/lucky-user.service";
|
||||
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
|
||||
import { Injectable } from "@nestjs/common";
|
||||
@@ -21,26 +17,18 @@ export class RegularBookingService extends BaseRegularBookingService {
|
||||
checkBookingAndDurationLimitsService: CheckBookingAndDurationLimitsService,
|
||||
prismaWriteService: PrismaWriteService,
|
||||
bookingRepository: PrismaBookingRepository,
|
||||
featuresRepository: PrismaFeaturesRepository,
|
||||
checkBookingLimitsService: CheckBookingLimitsService,
|
||||
hashedLinkService: HashedLinkService,
|
||||
luckyUserService: LuckyUserService,
|
||||
hostRepository: PrismaHostRepository,
|
||||
oooRepository: PrismaOOORepository,
|
||||
userRepository: PrismaUserRepository,
|
||||
attributeRepository: PrismaAttributeRepository
|
||||
userRepository: PrismaUserRepository
|
||||
) {
|
||||
super({
|
||||
cacheService,
|
||||
checkBookingAndDurationLimitsService,
|
||||
prismaClient: prismaWriteService.prisma as unknown as PrismaClient,
|
||||
bookingRepository,
|
||||
featuresRepository,
|
||||
checkBookingLimitsService,
|
||||
hashedLinkService,
|
||||
luckyUserService,
|
||||
hostRepository,
|
||||
oooRepository,
|
||||
userRepository,
|
||||
attributeRepository,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { PlatformPlan } from "@/modules/billing/types";
|
||||
import type { StripeService } from "@/modules/stripe/stripe.service";
|
||||
import Stripe from "stripe";
|
||||
|
||||
import { PlatformBilling, Team } from "@calcom/prisma/client";
|
||||
|
||||
export type BillingData = {
|
||||
|
||||
@@ -107,7 +107,7 @@ export class MembershipsRepository {
|
||||
},
|
||||
});
|
||||
|
||||
return userOrgAdminMemberships.map((m) => m.teamId);
|
||||
return userOrgAdminMemberships.map((m: { teamId: number }) => m.teamId);
|
||||
}
|
||||
|
||||
async createMembership(teamId: number, userId: number, role: MembershipRole, accepted: boolean) {
|
||||
|
||||
@@ -2,6 +2,8 @@ import { MembershipsRepository } from "@/modules/memberships/memberships.reposit
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { intersectionBy } from "lodash";
|
||||
|
||||
import type { Membership } from "@calcom/prisma/client";
|
||||
|
||||
@Injectable()
|
||||
export class MembershipsService {
|
||||
constructor(private readonly membershipsRepository: MembershipsRepository) {}
|
||||
@@ -16,8 +18,8 @@ export class MembershipsService {
|
||||
const secondUserMemberships = await this.membershipsRepository.findUserMemberships(secondUserId);
|
||||
|
||||
return intersectionBy(
|
||||
firstUserMemberships.filter((m) => m.accepted),
|
||||
secondUserMemberships.filter((m) => m.accepted),
|
||||
firstUserMemberships.filter((m: Membership) => m.accepted),
|
||||
secondUserMemberships.filter((m: Membership) => m.accepted),
|
||||
"teamId"
|
||||
);
|
||||
}
|
||||
|
||||
+13
-14
@@ -434,20 +434,19 @@ describe("Organizations Event Types Endpoints", () => {
|
||||
|
||||
const responseTeamEvent = responseBody.data.find((event) => event.teamId === team.id);
|
||||
expect(responseTeamEvent).toBeDefined();
|
||||
expect(responseTeamEvent?.hosts).toEqual([
|
||||
{
|
||||
userId: teammate1.id,
|
||||
name: teammate1.name,
|
||||
username: teammate1.username,
|
||||
avatarUrl: teammate1.avatarUrl,
|
||||
},
|
||||
{
|
||||
userId: teammate2.id,
|
||||
name: teammate2.name,
|
||||
username: teammate2.username,
|
||||
avatarUrl: teammate2.avatarUrl,
|
||||
},
|
||||
]);
|
||||
expect(responseTeamEvent?.hosts?.find((host) => host.userId === teammate1.id)).toEqual({
|
||||
userId: teammate1.id,
|
||||
name: teammate1.name,
|
||||
username: teammate1.username,
|
||||
avatarUrl: teammate1.avatarUrl,
|
||||
});
|
||||
|
||||
expect(responseTeamEvent?.hosts?.find((host) => host.userId === teammate2.id)).toEqual({
|
||||
userId: teammate2.id,
|
||||
name: teammate2.name,
|
||||
username: teammate2.username,
|
||||
avatarUrl: teammate2.avatarUrl,
|
||||
});
|
||||
|
||||
if (!responseTeamEvent) {
|
||||
throw new Error("Team event not found");
|
||||
|
||||
-3
@@ -122,7 +122,6 @@ describe("Organizations Teams Invite Endpoints", () => {
|
||||
.expect(404);
|
||||
});
|
||||
|
||||
|
||||
afterAll(async () => {
|
||||
await userRepositoryFixture.deleteByEmail(user.email);
|
||||
await organizationsRepositoryFixture.delete(org.id);
|
||||
@@ -131,5 +130,3 @@ describe("Organizations Teams Invite Endpoints", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
+5
-5
@@ -1,5 +1,9 @@
|
||||
import { API_VERSIONS_VALUES } from "@/lib/api-versions";
|
||||
import { OPTIONAL_API_KEY_HEADER, OPTIONAL_X_CAL_CLIENT_ID_HEADER, OPTIONAL_X_CAL_SECRET_KEY_HEADER } from "@/lib/docs/headers";
|
||||
import {
|
||||
OPTIONAL_API_KEY_HEADER,
|
||||
OPTIONAL_X_CAL_CLIENT_ID_HEADER,
|
||||
OPTIONAL_X_CAL_SECRET_KEY_HEADER,
|
||||
} from "@/lib/docs/headers";
|
||||
import { Roles } from "@/modules/auth/decorators/roles/roles.decorator";
|
||||
import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard";
|
||||
import { PlatformPlanGuard } from "@/modules/auth/guards/billing/platform-plan.guard";
|
||||
@@ -11,10 +15,8 @@ import { Controller, UseGuards, Post, Param, ParseIntPipe, HttpCode, HttpStatus
|
||||
import { ApiHeader, ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
|
||||
|
||||
import { SUCCESS_STATUS } from "@calcom/platform-constants";
|
||||
|
||||
import { TeamService } from "@calcom/platform-libraries";
|
||||
|
||||
|
||||
import { CreateInviteOutputDto } from "./outputs/invite.output";
|
||||
|
||||
@Controller({
|
||||
@@ -39,5 +41,3 @@ export class OrganizationsTeamsInviteController {
|
||||
return { status: SUCCESS_STATUS, data: result };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -11,7 +11,8 @@ export class InviteDataDto {
|
||||
@ApiProperty({
|
||||
description:
|
||||
"Complete invitation URL that can be shared with prospective members. Opens the signup page with the token and redirects to getting started after signup.",
|
||||
example: "http://app.cal.com/signup?token=f6a5c8b1d2e34c7f90a1b2c3d4e5f6a5b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2&callbackUrl=/getting-started",
|
||||
example:
|
||||
"http://app.cal.com/signup?token=f6a5c8b1d2e34c7f90a1b2c3d4e5f6a5b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2&callbackUrl=/getting-started",
|
||||
})
|
||||
inviteLink!: string;
|
||||
}
|
||||
@@ -23,5 +24,3 @@ export class CreateInviteOutputDto {
|
||||
@ApiProperty({ type: InviteDataDto })
|
||||
data!: InviteDataDto;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+17
-2
@@ -404,7 +404,7 @@ describe("OrganizationsTeamsWorkflowsController (E2E)", () => {
|
||||
invalidWorkflow.steps = [
|
||||
{
|
||||
stepNumber: 1,
|
||||
action: "sms_number",
|
||||
action: "cal_ai_phone_call",
|
||||
recipient: PHONE_NUMBER,
|
||||
template: REMINDER,
|
||||
verifiedPhoneId: verifiedPhoneId,
|
||||
@@ -439,6 +439,18 @@ describe("OrganizationsTeamsWorkflowsController (E2E)", () => {
|
||||
html: "<p>Reminder for your event {EVENT_NAME}.</p>",
|
||||
},
|
||||
},
|
||||
{
|
||||
stepNumber: 2,
|
||||
action: "sms_attendee",
|
||||
recipient: EMAIL,
|
||||
template: REMINDER,
|
||||
phoneRequired: false,
|
||||
sender: "updatedSender",
|
||||
message: {
|
||||
subject: "Update Upcoming: {EVENT_NAME}",
|
||||
text: "Update Reminder for your event {EVENT_NAME}.</p>",
|
||||
},
|
||||
},
|
||||
];
|
||||
return request(app.getHttpServer())
|
||||
.post(`${basePath}/routing-form`)
|
||||
@@ -459,12 +471,15 @@ describe("OrganizationsTeamsWorkflowsController (E2E)", () => {
|
||||
}
|
||||
|
||||
expect(responseBody.data.trigger.type).toEqual(sampleCreateWorkflowRoutingFormDto.trigger.type);
|
||||
expect(responseBody.data.steps).toHaveLength(sampleCreateWorkflowRoutingFormDto.steps.length);
|
||||
expect(responseBody.data.steps).toHaveLength(validWorkflow.steps.length);
|
||||
expect(responseBody.data.steps.find((step) => step.stepNumber === 1)?.id).toBeDefined();
|
||||
expect(responseBody.data.steps.find((step) => step.stepNumber === 1)?.sender).toEqual(
|
||||
"CalcomE2EStep1"
|
||||
);
|
||||
|
||||
expect(responseBody.data.steps.find((step) => step.stepNumber === 2)?.id).toBeDefined();
|
||||
expect(responseBody.data.steps.find((step) => step.action === "sms_attendee")).toBeDefined();
|
||||
|
||||
const trigger = sampleCreateWorkflowRoutingFormDto.trigger as OnFormSubmittedTriggerDto;
|
||||
expect(responseBody.data.trigger?.type).toEqual(trigger.type);
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
|
||||
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import type { Prisma } from "@calcom/prisma/client";
|
||||
|
||||
import type { Prisma, AttributeToUser, Membership, User, Profile } from "@calcom/prisma/client";
|
||||
|
||||
@Injectable()
|
||||
export class OrganizationsUsersRepository {
|
||||
@@ -60,7 +61,11 @@ export class OrganizationsUsersRepository {
|
||||
skip,
|
||||
take,
|
||||
});
|
||||
return attributeToUsersWithProfile.map((attributeToUser) => attributeToUser.member.user);
|
||||
return attributeToUsersWithProfile.map(
|
||||
(
|
||||
attributeToUser: AttributeToUser & { member: Membership & { user: User & { profiles: Profile[] } } }
|
||||
) => attributeToUser.member.user
|
||||
);
|
||||
}
|
||||
|
||||
async getOrganizationUsersByEmails(
|
||||
|
||||
@@ -1,36 +1,79 @@
|
||||
import type { OnModuleDestroy, OnModuleInit } from "@nestjs/common";
|
||||
import { Injectable, Logger } from "@nestjs/common";
|
||||
import { ConfigService } from "@nestjs/config";
|
||||
import { PrismaPg } from "@prisma/adapter-pg";
|
||||
import { Pool } from "pg";
|
||||
|
||||
import { PrismaClient } from "@calcom/prisma/client";
|
||||
|
||||
const DB_MAX_POOL_CONNECTION = 10;
|
||||
|
||||
export interface PrismaServiceOptions {
|
||||
readUrl?: string;
|
||||
maxReadConnections?: number;
|
||||
e2e?: boolean;
|
||||
type: "main" | "worker";
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class PrismaReadService implements OnModuleInit, OnModuleDestroy {
|
||||
private logger = new Logger("PrismaReadService");
|
||||
|
||||
public prisma: PrismaClient;
|
||||
public prisma!: PrismaClient;
|
||||
private pool!: Pool;
|
||||
private options!: PrismaServiceOptions;
|
||||
|
||||
constructor(readonly configService: ConfigService) {
|
||||
const dbUrl = configService.get("db.readUrl", { infer: true });
|
||||
constructor(private configService?: ConfigService) {
|
||||
if (configService) {
|
||||
// Use ConfigService defaults
|
||||
const readUrl = configService.get<string>("db.readUrl", { infer: true });
|
||||
const poolMax = parseInt(
|
||||
configService.get<number>("db.readPoolMax", { infer: true }) ?? DB_MAX_POOL_CONNECTION
|
||||
);
|
||||
const e2e = configService.get<boolean>("e2e", { infer: true }) ?? false;
|
||||
|
||||
this.prisma = new PrismaClient({
|
||||
datasources: {
|
||||
db: {
|
||||
url: dbUrl,
|
||||
},
|
||||
},
|
||||
this.setOptions({
|
||||
readUrl,
|
||||
maxReadConnections: poolMax,
|
||||
e2e,
|
||||
type: "main",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
setOptions(options: PrismaServiceOptions) {
|
||||
this.options = options;
|
||||
|
||||
const dbUrl = options.readUrl;
|
||||
const isE2E = options.e2e ?? false;
|
||||
|
||||
this.pool = new Pool({
|
||||
connectionString: dbUrl,
|
||||
max: isE2E ? 1 : options.maxReadConnections ?? DB_MAX_POOL_CONNECTION,
|
||||
idleTimeoutMillis: 300000,
|
||||
});
|
||||
|
||||
const adapter = new PrismaPg(this.pool);
|
||||
this.prisma = new PrismaClient({ adapter });
|
||||
}
|
||||
|
||||
async onModuleInit() {
|
||||
this.prisma.$connect();
|
||||
if (!this.prisma) return;
|
||||
|
||||
try {
|
||||
await this.prisma.$connect();
|
||||
this.logger.log("Connected to read database");
|
||||
} catch (error) {
|
||||
this.logger.error("Database connection failed", error);
|
||||
}
|
||||
}
|
||||
|
||||
async onModuleDestroy() {
|
||||
try {
|
||||
await this.prisma.$disconnect();
|
||||
if (this.prisma) await this.prisma.$disconnect();
|
||||
if (this.pool) await this.pool.end();
|
||||
} catch (error) {
|
||||
this.logger.error(error);
|
||||
this.logger.error("Error disconnecting from read database", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
|
||||
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
|
||||
import { Module } from "@nestjs/common";
|
||||
import { ConfigModule, ConfigService } from "@nestjs/config";
|
||||
|
||||
@Module({
|
||||
imports: [ConfigModule],
|
||||
providers: [
|
||||
{
|
||||
provide: PrismaReadService,
|
||||
useFactory: (config: ConfigService) => {
|
||||
const service = new PrismaReadService();
|
||||
service.setOptions({
|
||||
readUrl: config.get<string>("db.readUrl", { infer: true }),
|
||||
maxReadConnections: parseInt(config.get<number>("db.workerReadPoolMax", { infer: true })),
|
||||
e2e: config.get<boolean>("e2e", { infer: true }) ?? false,
|
||||
type: "worker",
|
||||
});
|
||||
return service;
|
||||
},
|
||||
inject: [ConfigService],
|
||||
},
|
||||
{
|
||||
provide: PrismaWriteService,
|
||||
useFactory: (config: ConfigService) => {
|
||||
const service = new PrismaWriteService();
|
||||
service.setOptions({
|
||||
writeUrl: config.get<string>("db.writeUrl", { infer: true }),
|
||||
maxWriteConnections: parseInt(config.get<number>("db.workerWritePoolMax", { infer: true })),
|
||||
e2e: config.get<boolean>("e2e", { infer: true }) ?? false,
|
||||
type: "worker",
|
||||
});
|
||||
return service;
|
||||
},
|
||||
inject: [ConfigService],
|
||||
},
|
||||
],
|
||||
exports: [PrismaReadService, PrismaWriteService],
|
||||
})
|
||||
export class PrismaWorkerModule {}
|
||||
@@ -1,36 +1,75 @@
|
||||
import { OnModuleDestroy, OnModuleInit } from "@nestjs/common";
|
||||
import { Injectable, Logger } from "@nestjs/common";
|
||||
import { OnModuleDestroy, OnModuleInit, Injectable, Logger } from "@nestjs/common";
|
||||
import { ConfigService } from "@nestjs/config";
|
||||
import { PrismaPg } from "@prisma/adapter-pg";
|
||||
import { Pool } from "pg";
|
||||
|
||||
import { PrismaClient } from "@calcom/prisma/client";
|
||||
|
||||
const DB_MAX_POOL_CONNECTION = 5;
|
||||
|
||||
export interface PrismaServiceOptions {
|
||||
writeUrl?: string;
|
||||
maxWriteConnections?: number;
|
||||
e2e?: boolean;
|
||||
type: "main" | "worker";
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class PrismaWriteService implements OnModuleInit, OnModuleDestroy {
|
||||
private logger = new Logger("PrismaWriteService");
|
||||
public prisma!: PrismaClient;
|
||||
private pool!: Pool;
|
||||
private options!: PrismaServiceOptions;
|
||||
|
||||
public prisma: PrismaClient;
|
||||
constructor(private configService?: ConfigService) {
|
||||
if (configService) {
|
||||
const writeUrl = configService.get<string>("db.writeUrl", { infer: true });
|
||||
const poolMax = parseInt(
|
||||
configService.get<number>("db.writePoolMax", { infer: true }) ?? DB_MAX_POOL_CONNECTION
|
||||
);
|
||||
const e2e = configService.get<boolean>("e2e", { infer: true }) ?? false;
|
||||
|
||||
constructor(readonly configService: ConfigService) {
|
||||
const dbUrl = configService.get("db.writeUrl", { infer: true });
|
||||
this.setOptions({
|
||||
writeUrl,
|
||||
maxWriteConnections: poolMax,
|
||||
e2e,
|
||||
type: "main",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.prisma = new PrismaClient({
|
||||
datasources: {
|
||||
db: {
|
||||
url: dbUrl,
|
||||
},
|
||||
},
|
||||
setOptions(options: PrismaServiceOptions) {
|
||||
this.options = options;
|
||||
|
||||
const dbUrl = options.writeUrl;
|
||||
const isE2E = options.e2e ?? false;
|
||||
this.pool = new Pool({
|
||||
connectionString: dbUrl,
|
||||
max: isE2E ? 1 : options.maxWriteConnections ?? DB_MAX_POOL_CONNECTION,
|
||||
idleTimeoutMillis: 300000,
|
||||
});
|
||||
|
||||
const adapter = new PrismaPg(this.pool);
|
||||
this.prisma = new PrismaClient({ adapter });
|
||||
}
|
||||
|
||||
async onModuleInit() {
|
||||
this.prisma.$connect();
|
||||
if (!this.prisma) return;
|
||||
|
||||
try {
|
||||
await this.prisma.$connect();
|
||||
this.logger.log("Connected to write database");
|
||||
} catch (error) {
|
||||
this.logger.error("Database connection failed", error);
|
||||
}
|
||||
}
|
||||
|
||||
async onModuleDestroy() {
|
||||
try {
|
||||
await this.prisma.$disconnect();
|
||||
if (this.prisma) await this.prisma.$disconnect();
|
||||
if (this.pool) await this.pool.end();
|
||||
} catch (error) {
|
||||
this.logger.error(error);
|
||||
this.logger.error("Error disconnecting from write database", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,40 @@
|
||||
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
|
||||
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
|
||||
import { Module } from "@nestjs/common";
|
||||
import { ConfigModule, ConfigService } from "@nestjs/config";
|
||||
|
||||
@Module({
|
||||
providers: [PrismaReadService, PrismaWriteService],
|
||||
imports: [ConfigModule],
|
||||
providers: [
|
||||
{
|
||||
provide: PrismaReadService,
|
||||
useFactory: (config: ConfigService) => {
|
||||
const service = new PrismaReadService();
|
||||
service.setOptions({
|
||||
readUrl: config.get<string>("db.readUrl", { infer: true }),
|
||||
maxReadConnections: parseInt(config.get<number>("db.readPoolMax", { infer: true })),
|
||||
e2e: config.get<boolean>("e2e", { infer: true }) ?? false,
|
||||
type: "main",
|
||||
});
|
||||
return service;
|
||||
},
|
||||
inject: [ConfigService],
|
||||
},
|
||||
{
|
||||
provide: PrismaWriteService,
|
||||
useFactory: (config: ConfigService) => {
|
||||
const service = new PrismaWriteService();
|
||||
service.setOptions({
|
||||
writeUrl: config.get<string>("db.writeUrl", { infer: true }),
|
||||
maxWriteConnections: parseInt(config.get<number>("db.writePoolMax", { infer: true })),
|
||||
e2e: config.get<boolean>("e2e", { infer: true }) ?? false,
|
||||
type: "main",
|
||||
});
|
||||
return service;
|
||||
},
|
||||
inject: [ConfigService],
|
||||
},
|
||||
],
|
||||
exports: [PrismaReadService, PrismaWriteService],
|
||||
})
|
||||
export class PrismaModule {}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Needed if ConfigService is used anywhere
|
||||
import appConfig from "@/config/app";
|
||||
import { AvailableSlotsModule } from "@/lib/modules/available-slots.module";
|
||||
import { PrismaModule } from "@/modules/prisma/prisma.module";
|
||||
import { PrismaWorkerModule } from "@/modules/prisma/prisma-worker.module";
|
||||
import { Module, Logger } from "@nestjs/common";
|
||||
import { ConfigModule } from "@nestjs/config";
|
||||
import { SentryModule } from "@sentry/nestjs/setup";
|
||||
@@ -15,7 +15,7 @@ import { SentryModule } from "@sentry/nestjs/setup";
|
||||
isGlobal: true,
|
||||
load: [appConfig],
|
||||
}),
|
||||
PrismaModule,
|
||||
PrismaWorkerModule,
|
||||
AvailableSlotsModule,
|
||||
],
|
||||
providers: [Logger],
|
||||
|
||||
@@ -118,7 +118,7 @@ export class StripeService {
|
||||
userId
|
||||
);
|
||||
|
||||
const credentialIdsToDelete = existingCredentials.map((item) => item.id);
|
||||
const credentialIdsToDelete = existingCredentials.map((item: Credential) => item.id);
|
||||
if (credentialIdsToDelete.length > 0) {
|
||||
await this.appsRepository.deleteAppCredentials(credentialIdsToDelete, userId);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ export const MembershipUserSelect: Prisma.UserSelect = {
|
||||
name: true,
|
||||
metadata: true,
|
||||
bio: true,
|
||||
};
|
||||
} satisfies Prisma.UserSelect;
|
||||
|
||||
@Injectable()
|
||||
export class TeamsMembershipsRepository {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
|
||||
import { Injectable, NotFoundException } from "@nestjs/common";
|
||||
|
||||
import { teamMetadataSchema } from "@calcom/platform-libraries";
|
||||
import { Prisma } from "@calcom/prisma/client";
|
||||
import type { Membership, Prisma } from "@calcom/prisma/client";
|
||||
|
||||
@Injectable()
|
||||
export class TeamsRepository {
|
||||
@@ -41,7 +41,7 @@ export class TeamsRepository {
|
||||
return [];
|
||||
}
|
||||
|
||||
return teamMembers.map((member) => member.userId);
|
||||
return teamMembers.map((member: Membership) => member.userId);
|
||||
}
|
||||
|
||||
async getTeamManagedUsersIds(teamId: number) {
|
||||
@@ -57,7 +57,7 @@ export class TeamsRepository {
|
||||
return [];
|
||||
}
|
||||
|
||||
return teamMembers.map((member) => member.userId);
|
||||
return teamMembers.map((member: Membership) => member.userId);
|
||||
}
|
||||
|
||||
async getTeamsUserIsMemberOf(userId: number) {
|
||||
|
||||
@@ -289,7 +289,7 @@ export class UsersRepository {
|
||||
user: true,
|
||||
},
|
||||
});
|
||||
return profiles.map((profile) => profile.user);
|
||||
return profiles.map((profile: Profile & { user: User }) => profile.user);
|
||||
}
|
||||
|
||||
async setDefaultConferencingApp(userId: number, appSlug?: string, appLink?: string) {
|
||||
|
||||
@@ -7,8 +7,12 @@ import {
|
||||
EMAIL_ADDRESS,
|
||||
EMAIL_ATTENDEE,
|
||||
FORM_ALLOWED_STEP_ACTIONS,
|
||||
SMS_NUMBER,
|
||||
WorkflowEmailAddressStepDto,
|
||||
WorkflowEmailAttendeeStepDto,
|
||||
WorkflowPhoneNumberStepDto,
|
||||
WorkflowPhoneAttendeeStepDto,
|
||||
SMS_ATTENDEE,
|
||||
} from "./workflow-step.input";
|
||||
import {
|
||||
RoutingFormWorkflowTriggerDto,
|
||||
@@ -85,6 +89,8 @@ export class CreateFormWorkflowDto {
|
||||
oneOf: [
|
||||
{ $ref: getSchemaPath(WorkflowEmailAddressStepDto) },
|
||||
{ $ref: getSchemaPath(WorkflowEmailAttendeeStepDto) },
|
||||
{ $ref: getSchemaPath(WorkflowPhoneAttendeeStepDto) },
|
||||
{ $ref: getSchemaPath(WorkflowPhoneNumberStepDto) },
|
||||
],
|
||||
type: "array",
|
||||
})
|
||||
@@ -99,8 +105,15 @@ export class CreateFormWorkflowDto {
|
||||
subTypes: [
|
||||
{ value: WorkflowEmailAddressStepDto, name: EMAIL_ADDRESS },
|
||||
{ value: WorkflowEmailAttendeeStepDto, name: EMAIL_ATTENDEE },
|
||||
{ value: WorkflowPhoneAttendeeStepDto, name: SMS_ATTENDEE },
|
||||
{ value: WorkflowPhoneNumberStepDto, name: SMS_NUMBER },
|
||||
],
|
||||
},
|
||||
})
|
||||
steps!: (WorkflowEmailAddressStepDto | WorkflowEmailAttendeeStepDto)[];
|
||||
steps!: (
|
||||
| WorkflowEmailAddressStepDto
|
||||
| WorkflowEmailAttendeeStepDto
|
||||
| WorkflowPhoneAttendeeStepDto
|
||||
| WorkflowPhoneNumberStepDto
|
||||
)[];
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ import {
|
||||
EMAIL_ADDRESS,
|
||||
EMAIL_ATTENDEE,
|
||||
FORM_ALLOWED_STEP_ACTIONS,
|
||||
SMS_ATTENDEE,
|
||||
SMS_NUMBER,
|
||||
UpdateEmailAddressWorkflowStepDto,
|
||||
UpdateEmailAttendeeWorkflowStepDto,
|
||||
UpdateEmailHostWorkflowStepDto,
|
||||
@@ -75,6 +77,8 @@ export class UpdateFormWorkflowDto {
|
||||
oneOf: [
|
||||
{ $ref: getSchemaPath(UpdateEmailAddressWorkflowStepDto) },
|
||||
{ $ref: getSchemaPath(UpdateEmailAttendeeWorkflowStepDto) },
|
||||
{ $ref: getSchemaPath(UpdatePhoneAttendeeWorkflowStepDto) },
|
||||
{ $ref: getSchemaPath(UpdatePhoneNumberWorkflowStepDto) },
|
||||
],
|
||||
type: "array",
|
||||
})
|
||||
@@ -90,8 +94,15 @@ export class UpdateFormWorkflowDto {
|
||||
subTypes: [
|
||||
{ value: UpdateEmailAddressWorkflowStepDto, name: EMAIL_ADDRESS },
|
||||
{ value: UpdateEmailAttendeeWorkflowStepDto, name: EMAIL_ATTENDEE },
|
||||
{ value: UpdatePhoneAttendeeWorkflowStepDto, name: SMS_ATTENDEE },
|
||||
{ value: UpdatePhoneNumberWorkflowStepDto, name: SMS_NUMBER },
|
||||
],
|
||||
},
|
||||
})
|
||||
steps?: (UpdateEmailAddressWorkflowStepDto | UpdateEmailAttendeeWorkflowStepDto)[];
|
||||
steps?: (
|
||||
| UpdateEmailAddressWorkflowStepDto
|
||||
| UpdateEmailAttendeeWorkflowStepDto
|
||||
| UpdatePhoneNumberWorkflowStepDto
|
||||
| UpdatePhoneAttendeeWorkflowStepDto
|
||||
)[];
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ export const STEP_ACTIONS = [
|
||||
CAL_AI_PHONE_CALL,
|
||||
] as const;
|
||||
|
||||
export const FORM_ALLOWED_STEP_ACTIONS = [EMAIL_ATTENDEE, EMAIL_ADDRESS] as const;
|
||||
export const FORM_ALLOWED_STEP_ACTIONS = [EMAIL_ATTENDEE, EMAIL_ADDRESS, SMS_ATTENDEE, SMS_NUMBER] as const;
|
||||
|
||||
export const STEP_ACTIONS_TO_ENUM = {
|
||||
[EMAIL_HOST]: WorkflowActions.EMAIL_HOST,
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
"jsx": "react-jsx",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"],
|
||||
"@prisma/client/*": ["./client/*"],
|
||||
"@calcom/prisma/client": ["../../../packages/prisma/generated/prisma/client"],
|
||||
"@calcom/prisma/enums": ["../../../packages/prisma/enums"],
|
||||
"@calcom/platform-constants": ["../../../packages/platform/constants/index.ts"],
|
||||
"@calcom/platform-types": ["../../../packages/platform/types/index.ts"],
|
||||
|
||||
@@ -3,6 +3,7 @@ import { unstable_cache } from "next/cache";
|
||||
|
||||
import { eventTypeMetaDataSchemaWithTypedApps } from "@calcom/app-store/zod-utils";
|
||||
import { getTeamData } from "@calcom/features/ee/teams/lib/getTeamData";
|
||||
import { TeamRepository } from "@calcom/features/ee/teams/repositories/TeamRepository";
|
||||
import {
|
||||
getEventTypeHosts,
|
||||
getProfileFromEvent,
|
||||
@@ -11,10 +12,9 @@ import {
|
||||
} from "@calcom/features/eventtypes/lib/getPublicEvent";
|
||||
import { getTeamEventType } from "@calcom/features/eventtypes/lib/getTeamEventType";
|
||||
import { FeaturesRepository } from "@calcom/features/flags/features.repository";
|
||||
import { UserRepository } from "@calcom/features/users/repositories/UserRepository";
|
||||
import { NEXTJS_CACHE_TTL } from "@calcom/lib/constants";
|
||||
import { getPlaceholderAvatar } from "@calcom/lib/defaultAvatarImage";
|
||||
import { TeamRepository } from "@calcom/features/ee/teams/repositories/TeamRepository";
|
||||
import { UserRepository } from "@calcom/features/users/repositories/UserRepository";
|
||||
import { prisma } from "@calcom/prisma";
|
||||
import type { Prisma } from "@calcom/prisma/client";
|
||||
import type { SchedulingType } from "@calcom/prisma/enums";
|
||||
|
||||
@@ -12,7 +12,7 @@ import { MembershipRole } from "@calcom/prisma/enums";
|
||||
import { buildLegacyRequest } from "@lib/buildLegacyCtx";
|
||||
|
||||
import { validStatuses } from "~/bookings/lib/validStatuses";
|
||||
import BookingsList from "~/bookings/views/bookings-listing-view";
|
||||
import BookingsList from "~/bookings/views/bookings-view";
|
||||
|
||||
const querySchema = z.object({
|
||||
status: z.enum(validStatuses),
|
||||
|
||||
@@ -23,8 +23,14 @@ const ServerPage = async ({ searchParams: _searchParams }: ServerPageProps) => {
|
||||
const session = await getServerSession({ req: buildLegacyRequest(await headers(), await cookies()) });
|
||||
const searchParams = await _searchParams;
|
||||
const token = Array.isArray(searchParams?.token) ? searchParams.token[0] : searchParams?.token;
|
||||
const autoAccept = Array.isArray(searchParams?.autoAccept) ? searchParams.autoAccept[0] : searchParams?.autoAccept;
|
||||
const callbackUrl = token ? `/teams?token=${encodeURIComponent(token)}${autoAccept ? `&autoAccept=${encodeURIComponent(autoAccept)}` : ''}` : null;
|
||||
const autoAccept = Array.isArray(searchParams?.autoAccept)
|
||||
? searchParams.autoAccept[0]
|
||||
: searchParams?.autoAccept;
|
||||
const callbackUrl = token
|
||||
? `/teams?token=${encodeURIComponent(token)}${
|
||||
autoAccept ? `&autoAccept=${encodeURIComponent(autoAccept)}` : ""
|
||||
}`
|
||||
: null;
|
||||
|
||||
if (!session) {
|
||||
redirect(callbackUrl ? `/auth/login?callbackUrl=${callbackUrl}` : "/auth/login");
|
||||
|
||||
@@ -3,9 +3,9 @@ import type { Session } from "next-auth";
|
||||
import { unstable_cache } from "next/cache";
|
||||
|
||||
import { TeamsListing } from "@calcom/features/ee/teams/components/TeamsListing";
|
||||
import { PermissionCheckService } from "@calcom/features/pbac/services/permission-check.service";
|
||||
import { TeamRepository } from "@calcom/features/ee/teams/repositories/TeamRepository";
|
||||
import { TeamService } from "@calcom/features/ee/teams/services/teamService";
|
||||
import { PermissionCheckService } from "@calcom/features/pbac/services/permission-check.service";
|
||||
import prisma from "@calcom/prisma";
|
||||
import { MembershipRole } from "@calcom/prisma/enums";
|
||||
|
||||
@@ -33,7 +33,9 @@ export const ServerTeamsListing = async ({
|
||||
session: Session;
|
||||
}) => {
|
||||
const token = Array.isArray(searchParams?.token) ? searchParams.token[0] : searchParams?.token;
|
||||
const autoAccept = Array.isArray(searchParams?.autoAccept) ? searchParams.autoAccept[0] : searchParams?.autoAccept;
|
||||
const autoAccept = Array.isArray(searchParams?.autoAccept)
|
||||
? searchParams.autoAccept[0]
|
||||
: searchParams?.autoAccept;
|
||||
const userId = session.user.id;
|
||||
let invitationAccepted = false;
|
||||
|
||||
@@ -42,7 +44,7 @@ export const ServerTeamsListing = async ({
|
||||
|
||||
if (token) {
|
||||
try {
|
||||
if (autoAccept === "true") {
|
||||
if (autoAccept === "true") {
|
||||
await TeamService.acceptInvitationByToken(token, userId);
|
||||
invitationAccepted = true;
|
||||
} else {
|
||||
|
||||
@@ -308,7 +308,7 @@ const WeightedAttributesSelector = ({
|
||||
/>
|
||||
</div>
|
||||
<div className="bg-muted mt-1 rounded-xl p-2">
|
||||
{!!attributeIdForWeights ? (
|
||||
{attributeIdForWeights ? (
|
||||
<SelectField
|
||||
size="sm"
|
||||
containerClassName="data-testid-select-router"
|
||||
|
||||
@@ -76,7 +76,6 @@ export default function RoutingForms({ appUrl }: { appUrl: string }) {
|
||||
|
||||
useEffect(() => {
|
||||
hookForm.reset({});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
const filters = getTeamsFiltersFromQuery(routerQuery);
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@ import { cookies, headers } from "next/headers";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
||||
import { APP_NAME } from "@calcom/lib/constants";
|
||||
import { UserRepository } from "@calcom/features/users/repositories/UserRepository";
|
||||
import { APP_NAME } from "@calcom/lib/constants";
|
||||
import prisma from "@calcom/prisma";
|
||||
import { meRouter } from "@calcom/trpc/server/routers/viewer/me/_router";
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import { _generateMetadata } from "app/_utils";
|
||||
import { cookies, headers } from "next/headers";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
||||
import { APP_NAME } from "@calcom/lib/constants";
|
||||
|
||||
import { buildLegacyRequest } from "@lib/buildLegacyCtx";
|
||||
|
||||
import { OnboardingView } from "~/onboarding/getting-started/onboarding-view";
|
||||
|
||||
export const generateMetadata = async () => {
|
||||
return await _generateMetadata(
|
||||
(t) => `${APP_NAME} - ${t("getting_started")}`,
|
||||
() => "",
|
||||
true,
|
||||
undefined,
|
||||
"/onboarding/getting-started"
|
||||
);
|
||||
};
|
||||
|
||||
const ServerPage = async () => {
|
||||
const session = await getServerSession({ req: buildLegacyRequest(await headers(), await cookies()) });
|
||||
|
||||
if (!session?.user?.id) {
|
||||
return redirect("/auth/login");
|
||||
}
|
||||
|
||||
// Hello {username} || there. Not sure how to do this nicely with i18n just yet
|
||||
const userName = session.user.name || "there";
|
||||
const userEmail = session.user.email || "";
|
||||
|
||||
return <OnboardingView userName={userName} userEmail={userEmail} />;
|
||||
};
|
||||
|
||||
export default ServerPage;
|
||||
@@ -0,0 +1,15 @@
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
import { FeaturesRepository } from "@calcom/features/flags/features.repository";
|
||||
import { prisma } from "@calcom/prisma";
|
||||
|
||||
export default async function OnboardingLayout({ children }: { children: React.ReactNode }) {
|
||||
const featuresRepository = new FeaturesRepository(prisma);
|
||||
const isOnboardingV3Enabled = await featuresRepository.checkIfFeatureIsEnabledGlobally("onboarding-v3");
|
||||
|
||||
if (!isOnboardingV3Enabled) {
|
||||
redirect("/getting-started");
|
||||
}
|
||||
|
||||
return <>{children}</>;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { createRouterCaller } from "app/_trpc/context";
|
||||
import { _generateMetadata } from "app/_utils";
|
||||
import { cookies, headers } from "next/headers";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
||||
import { APP_NAME } from "@calcom/lib/constants";
|
||||
|
||||
import { buildLegacyRequest } from "@lib/buildLegacyCtx";
|
||||
|
||||
import { OrganizationBrandView } from "~/onboarding/organization/brand/organization-brand-view";
|
||||
|
||||
export const generateMetadata = async () => {
|
||||
return await _generateMetadata(
|
||||
(t) => `${APP_NAME} - ${t("organization_brand")}`,
|
||||
() => "",
|
||||
true,
|
||||
undefined,
|
||||
"/onboarding/organization/brand"
|
||||
);
|
||||
};
|
||||
|
||||
const ServerPage = async () => {
|
||||
const session = await getServerSession({ req: buildLegacyRequest(await headers(), await cookies()) });
|
||||
|
||||
if (!session?.user?.id) {
|
||||
return redirect("/auth/login");
|
||||
}
|
||||
|
||||
const userEmail = session.user.email || "";
|
||||
|
||||
return <OrganizationBrandView userEmail={userEmail} />;
|
||||
};
|
||||
|
||||
export default ServerPage;
|
||||
@@ -0,0 +1,34 @@
|
||||
import { _generateMetadata } from "app/_utils";
|
||||
import { cookies, headers } from "next/headers";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
||||
import { APP_NAME } from "@calcom/lib/constants";
|
||||
|
||||
import { buildLegacyRequest } from "@lib/buildLegacyCtx";
|
||||
|
||||
import { OrganizationDetailsView } from "~/onboarding/organization/details/organization-details-view";
|
||||
|
||||
export const generateMetadata = async () => {
|
||||
return await _generateMetadata(
|
||||
(t) => `${APP_NAME} - ${t("organization_details")}`,
|
||||
() => "",
|
||||
true,
|
||||
undefined,
|
||||
"/onboarding/organization/details"
|
||||
);
|
||||
};
|
||||
|
||||
const ServerPage = async () => {
|
||||
const session = await getServerSession({ req: buildLegacyRequest(await headers(), await cookies()) });
|
||||
|
||||
if (!session?.user?.id) {
|
||||
return redirect("/auth/login");
|
||||
}
|
||||
|
||||
const userEmail = session.user.email || "";
|
||||
|
||||
return <OrganizationDetailsView userEmail={userEmail} />;
|
||||
};
|
||||
|
||||
export default ServerPage;
|
||||
@@ -0,0 +1,34 @@
|
||||
import { _generateMetadata } from "app/_utils";
|
||||
import { cookies, headers } from "next/headers";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
||||
import { APP_NAME } from "@calcom/lib/constants";
|
||||
|
||||
import { buildLegacyRequest } from "@lib/buildLegacyCtx";
|
||||
|
||||
import { OrganizationInviteView } from "~/onboarding/organization/invite/organization-invite-view";
|
||||
|
||||
export const generateMetadata = async () => {
|
||||
return await _generateMetadata(
|
||||
(t) => `${APP_NAME} - ${t("invite_teammates")}`,
|
||||
() => "",
|
||||
true,
|
||||
undefined,
|
||||
"/onboarding/organization/invite"
|
||||
);
|
||||
};
|
||||
|
||||
const ServerPage = async () => {
|
||||
const session = await getServerSession({ req: buildLegacyRequest(await headers(), await cookies()) });
|
||||
|
||||
if (!session?.user?.id) {
|
||||
return redirect("/auth/login");
|
||||
}
|
||||
|
||||
const userEmail = session.user.email || "";
|
||||
|
||||
return <OrganizationInviteView userEmail={userEmail} />;
|
||||
};
|
||||
|
||||
export default ServerPage;
|
||||
@@ -0,0 +1,35 @@
|
||||
import { createRouterCaller } from "app/_trpc/context";
|
||||
import { _generateMetadata } from "app/_utils";
|
||||
import { cookies, headers } from "next/headers";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
||||
import { APP_NAME } from "@calcom/lib/constants";
|
||||
|
||||
import { buildLegacyRequest } from "@lib/buildLegacyCtx";
|
||||
|
||||
import { OrganizationTeamsView } from "~/onboarding/organization/teams/organization-teams-view";
|
||||
|
||||
export const generateMetadata = async () => {
|
||||
return await _generateMetadata(
|
||||
(t) => `${APP_NAME} - ${t("organization_teams")}`,
|
||||
() => "",
|
||||
true,
|
||||
undefined,
|
||||
"/onboarding/organization/teams"
|
||||
);
|
||||
};
|
||||
|
||||
const ServerPage = async () => {
|
||||
const session = await getServerSession({ req: buildLegacyRequest(await headers(), await cookies()) });
|
||||
|
||||
if (!session?.user?.id) {
|
||||
return redirect("/auth/login");
|
||||
}
|
||||
|
||||
const userEmail = session.user.email || "";
|
||||
|
||||
return <OrganizationTeamsView userEmail={userEmail} />;
|
||||
};
|
||||
|
||||
export default ServerPage;
|
||||
+1
-1
@@ -4,8 +4,8 @@ import { z } from "zod";
|
||||
|
||||
import LicenseRequired from "@calcom/features/ee/common/components/LicenseRequired";
|
||||
import { OrgForm } from "@calcom/features/ee/organizations/pages/settings/admin/AdminOrgEditPage";
|
||||
import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader";
|
||||
import { OrganizationRepository } from "@calcom/features/ee/organizations/repositories/OrganizationRepository";
|
||||
import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader";
|
||||
|
||||
const orgIdSchema = z.object({ id: z.coerce.number() });
|
||||
|
||||
|
||||
-2
@@ -369,7 +369,6 @@ const TeamListCollapsible = ({ teamFeatures }: { teamFeatures?: Record<number, T
|
||||
const tabMembers = Array.from(document.getElementsByTagName("a")).filter(
|
||||
(bottom) => bottom.dataset.testid === "vertical-tab-Members"
|
||||
)[1];
|
||||
// eslint-disable-next-line @calcom/eslint/no-scroll-into-view-embed -- Settings layout isn't embedded
|
||||
tabMembers?.scrollIntoView({ behavior: "smooth" });
|
||||
}, 100);
|
||||
}
|
||||
@@ -568,7 +567,6 @@ const SettingsSidebarContainer = ({
|
||||
const tabMembers = Array.from(document.getElementsByTagName("a")).filter(
|
||||
(bottom) => bottom.dataset.testid === "vertical-tab-Members"
|
||||
)[1];
|
||||
// eslint-disable-next-line @calcom/eslint/no-scroll-into-view-embed -- Settings layout isn't embedded
|
||||
tabMembers?.scrollIntoView({ behavior: "smooth" });
|
||||
}, 100);
|
||||
}
|
||||
|
||||
+1
-1
@@ -2,9 +2,9 @@ import type { PageProps } from "app/_types";
|
||||
import { getTranslate, _generateMetadata } from "app/_utils";
|
||||
|
||||
import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader";
|
||||
import { WebhookRepository } from "@calcom/features/webhooks/lib/repository/WebhookRepository";
|
||||
import { EditWebhookView } from "@calcom/features/webhooks/pages/webhook-edit-view";
|
||||
import { APP_NAME } from "@calcom/lib/constants";
|
||||
import { WebhookRepository } from "@calcom/features/webhooks/lib/repository/WebhookRepository";
|
||||
|
||||
export const generateMetadata = async ({ params }: { params: Promise<{ id: string }> }) =>
|
||||
await _generateMetadata(
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
import { _generateMetadata, getTranslate } from "app/_utils";
|
||||
import { unstable_cache } from "next/cache";
|
||||
import { notFound } from "next/navigation";
|
||||
import { revalidatePath } from "next/cache";
|
||||
import { notFound } from "next/navigation";
|
||||
|
||||
import type { AppFlags } from "@calcom/features/flags/config";
|
||||
import { FeaturesRepository } from "@calcom/features/flags/features.repository";
|
||||
|
||||
+1
-1
@@ -2,8 +2,8 @@ import { _generateMetadata, getTranslate } from "app/_utils";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
import { OtherTeamsListing } from "@calcom/features/ee/organizations/pages/components/OtherTeamsListing";
|
||||
import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader";
|
||||
import { OrganizationRepository } from "@calcom/features/ee/organizations/repositories/OrganizationRepository";
|
||||
import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader";
|
||||
|
||||
import { validateUserHasOrg } from "../../../actions/validateUserHasOrg";
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ const resolveEndpoint = (links: any) => {
|
||||
// to the correct API endpoints.
|
||||
// - viewer.me - 2 segment paths like this are for logged in requests
|
||||
// - viewer.public.i18n - 3 segments paths can be public or authed
|
||||
// - viewer.eventTypes.heavy.create - 4 segments paths for heavy sub-router
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
return (ctx: any) => {
|
||||
const parts = ctx.op.path.split(".");
|
||||
@@ -24,11 +23,7 @@ const resolveEndpoint = (links: any) => {
|
||||
if (parts.length == 2) {
|
||||
endpoint = parts[0] as keyof typeof links;
|
||||
path = parts[1];
|
||||
} else if (parts.length >= 3 && parts[2] === "heavy") {
|
||||
endpoint = parts[1] + "/heavy" as keyof typeof links;
|
||||
path = parts[3];
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
endpoint = parts[1] as keyof typeof links;
|
||||
path = parts.splice(2, parts.length - 2).join(".");
|
||||
}
|
||||
|
||||
@@ -6,11 +6,14 @@ import { z } from "zod";
|
||||
|
||||
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
||||
import { CalendarCache } from "@calcom/features/calendar-cache/calendar-cache";
|
||||
import { getCalendarCredentials, getConnectedCalendars } from "@calcom/features/calendars/lib/CalendarManager";
|
||||
import {
|
||||
getCalendarCredentials,
|
||||
getConnectedCalendars,
|
||||
} from "@calcom/features/calendars/lib/CalendarManager";
|
||||
import { UserRepository } from "@calcom/features/users/repositories/UserRepository";
|
||||
import { HttpError } from "@calcom/lib/http-error";
|
||||
import notEmpty from "@calcom/lib/notEmpty";
|
||||
import { SelectedCalendarRepository } from "@calcom/lib/server/repository/selectedCalendar";
|
||||
import { UserRepository } from "@calcom/features/users/repositories/UserRepository";
|
||||
import prisma from "@calcom/prisma";
|
||||
|
||||
import { buildLegacyRequest } from "@lib/buildLegacyCtx";
|
||||
|
||||
@@ -13,7 +13,7 @@ async function handler(req: NextRequest) {
|
||||
let appDirRequestBody;
|
||||
try {
|
||||
appDirRequestBody = await req.json();
|
||||
} catch (error) {
|
||||
} catch {
|
||||
return NextResponse.json({ success: false, message: "Invalid JSON" }, { status: 400 });
|
||||
}
|
||||
const bookingData = bookingCancelWithCsrfSchema.parse(appDirRequestBody);
|
||||
@@ -26,11 +26,20 @@ async function handler(req: NextRequest) {
|
||||
cookieStore.delete("calcom.csrf_token");
|
||||
|
||||
const session = await getServerSession({ req: buildLegacyRequest(await headers(), await cookies()) });
|
||||
|
||||
const result = await handleCancelBooking({
|
||||
bookingData,
|
||||
userId: session?.user?.id || -1,
|
||||
});
|
||||
|
||||
// const bookingCancelService = getBookingCancelService();
|
||||
// const result = await bookingCancelService.cancelBooking({
|
||||
// bookingData: bookingData,
|
||||
// bookingMeta: {
|
||||
// userId: session?.user?.id || -1,
|
||||
// },
|
||||
// });
|
||||
|
||||
const statusCode = result.success ? 200 : 400;
|
||||
|
||||
return NextResponse.json(result, { status: statusCode });
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { NextRequest } from "next/server";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
import { BookingRepository } from "@calcom/features/bookings/repositories/BookingRepository";
|
||||
import { DefaultAdapterFactory } from "@calcom/features/calendar-subscription/adapters/AdaptersFactory";
|
||||
import { CalendarSubscriptionService } from "@calcom/features/calendar-subscription/lib/CalendarSubscriptionService";
|
||||
import { CalendarCacheEventRepository } from "@calcom/features/calendar-subscription/lib/cache/CalendarCacheEventRepository";
|
||||
@@ -8,7 +9,6 @@ import { CalendarCacheEventService } from "@calcom/features/calendar-subscriptio
|
||||
import { CalendarSyncService } from "@calcom/features/calendar-subscription/lib/sync/CalendarSyncService";
|
||||
import { FeaturesRepository } from "@calcom/features/flags/features.repository";
|
||||
import { SelectedCalendarRepository } from "@calcom/lib/server/repository/SelectedCalendarRepository";
|
||||
import { BookingRepository } from "@calcom/features/bookings/repositories/BookingRepository";
|
||||
import { prisma } from "@calcom/prisma";
|
||||
import { defaultResponderForAppDir } from "@calcom/web/app/api/defaultResponderForAppDir";
|
||||
|
||||
|
||||
@@ -6,13 +6,13 @@
|
||||
import type { NextRequest } from "next/server";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
import GoogleCalendarService from "@calcom/app-store/googlecalendar/lib/CalendarService";
|
||||
import { CalendarAppDelegationCredentialInvalidGrantError } from "@calcom/lib/CalendarAppError";
|
||||
import { findUniqueDelegationCalendarCredential } from "@calcom/app-store/delegationCredential";
|
||||
import GoogleCalendarService from "@calcom/app-store/googlecalendar/lib/CalendarService";
|
||||
import { CredentialRepository } from "@calcom/features/credentials/repositories/CredentialRepository";
|
||||
import { CalendarAppDelegationCredentialInvalidGrantError } from "@calcom/lib/CalendarAppError";
|
||||
import { HttpError } from "@calcom/lib/http-error";
|
||||
import logger from "@calcom/lib/logger";
|
||||
import { safeStringify } from "@calcom/lib/safeStringify";
|
||||
import { CredentialRepository } from "@calcom/features/credentials/repositories/CredentialRepository";
|
||||
import { SelectedCalendarRepository } from "@calcom/lib/server/repository/selectedCalendar";
|
||||
import type { CredentialForCalendarServiceWithEmail } from "@calcom/types/Credential";
|
||||
import type { Ensure } from "@calcom/types/utils";
|
||||
|
||||
@@ -9,15 +9,15 @@ import {
|
||||
sendDailyVideoRecordingEmails,
|
||||
sendDailyVideoTranscriptEmails,
|
||||
} from "@calcom/emails/daily-video-emails";
|
||||
import {
|
||||
getAllTranscriptsAccessLinkFromMeetingId,
|
||||
submitBatchProcessorTranscriptionJob,
|
||||
} from "@calcom/features/conferencing/lib/videoClient";
|
||||
import { WEBAPP_URL } from "@calcom/lib/constants";
|
||||
import { getTeamIdFromEventType } from "@calcom/lib/getTeamIdFromEventType";
|
||||
import { HttpError } from "@calcom/lib/http-error";
|
||||
import logger from "@calcom/lib/logger";
|
||||
import { safeStringify } from "@calcom/lib/safeStringify";
|
||||
import {
|
||||
getAllTranscriptsAccessLinkFromMeetingId,
|
||||
submitBatchProcessorTranscriptionJob,
|
||||
} from "@calcom/features/conferencing/lib/videoClient";
|
||||
import { generateVideoToken } from "@calcom/lib/videoTokens";
|
||||
import prisma from "@calcom/prisma";
|
||||
import { getBooking } from "@calcom/web/lib/daily-webhook/getBooking";
|
||||
|
||||
@@ -6,11 +6,11 @@ import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
||||
import { BillingPlanService } from "@calcom/features/ee/billing/domain/billing-plans";
|
||||
import type { Contact } from "@calcom/features/ee/support/lib/intercom/intercom";
|
||||
import { intercom } from "@calcom/features/ee/support/lib/intercom/intercom";
|
||||
import { MembershipRepository } from "@calcom/features/membership/repositories/MembershipRepository";
|
||||
import { UserRepository } from "@calcom/features/users/repositories/UserRepository";
|
||||
import { WEBAPP_URL, WEBSITE_URL } from "@calcom/lib/constants";
|
||||
import logger from "@calcom/lib/logger";
|
||||
import { safeStringify } from "@calcom/lib/safeStringify";
|
||||
import { MembershipRepository } from "@calcom/features/membership/repositories/MembershipRepository";
|
||||
import { UserRepository } from "@calcom/features/users/repositories/UserRepository";
|
||||
import { prisma } from "@calcom/prisma";
|
||||
|
||||
import { buildLegacyRequest } from "@lib/buildLegacyCtx";
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { Params } from "app/_types";
|
||||
import type { NextRequest } from "next/server";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
import { BookingRepository } from "@calcom/features/bookings/repositories/BookingRepository";
|
||||
import type { CalendarSubscriptionProvider } from "@calcom/features/calendar-subscription/adapters/AdaptersFactory";
|
||||
import { DefaultAdapterFactory } from "@calcom/features/calendar-subscription/adapters/AdaptersFactory";
|
||||
import { CalendarSubscriptionService } from "@calcom/features/calendar-subscription/lib/CalendarSubscriptionService";
|
||||
@@ -11,7 +12,6 @@ import { CalendarSyncService } from "@calcom/features/calendar-subscription/lib/
|
||||
import { FeaturesRepository } from "@calcom/features/flags/features.repository";
|
||||
import logger from "@calcom/lib/logger";
|
||||
import { SelectedCalendarRepository } from "@calcom/lib/server/repository/SelectedCalendarRepository";
|
||||
import { BookingRepository } from "@calcom/features/bookings/repositories/BookingRepository";
|
||||
import { prisma } from "@calcom/prisma";
|
||||
import { defaultResponderForAppDir } from "@calcom/web/app/api/defaultResponderForAppDir";
|
||||
|
||||
|
||||
@@ -696,7 +696,15 @@ describe("Retell AI Webhook Handler", () => {
|
||||
describe("Web Call Tests", () => {
|
||||
const mockAgent: Pick<
|
||||
Agent,
|
||||
"id" | "name" | "providerAgentId" | "enabled" | "userId" | "teamId" | "createdAt" | "updatedAt" | "inboundEventTypeId"
|
||||
| "id"
|
||||
| "name"
|
||||
| "providerAgentId"
|
||||
| "enabled"
|
||||
| "userId"
|
||||
| "teamId"
|
||||
| "createdAt"
|
||||
| "updatedAt"
|
||||
| "inboundEventTypeId"
|
||||
> = {
|
||||
id: "agent-123",
|
||||
name: "Test Agent",
|
||||
|
||||
Vendored
+1
-1
@@ -2,8 +2,8 @@
|
||||
|
||||
import { revalidateTag, unstable_cache } from "next/cache";
|
||||
|
||||
import { NEXTJS_CACHE_TTL } from "@calcom/lib/constants";
|
||||
import { MembershipRepository } from "@calcom/features/membership/repositories/MembershipRepository";
|
||||
import { NEXTJS_CACHE_TTL } from "@calcom/lib/constants";
|
||||
|
||||
const CACHE_TAGS = {
|
||||
HAS_TEAM_PLAN: "MembershipRepository.findFirstAcceptedMembershipByUserId",
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import z from "zod";
|
||||
|
||||
import type { PaymentPageProps } from "@calcom/features/ee/payments/pages/payment";
|
||||
import { useBookingSuccessRedirect } from "@calcom/features/bookings/lib/bookingSuccessRedirect";
|
||||
import type { PaymentPageProps } from "@calcom/features/ee/payments/pages/payment";
|
||||
import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams";
|
||||
import { useCopy } from "@calcom/lib/hooks/useCopy";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
|
||||
@@ -115,7 +115,9 @@ export function getEditEventActions(context: BookingActionContext): ActionType[]
|
||||
icon: "clock",
|
||||
label: t("reschedule_booking"),
|
||||
href: `/reschedule/${booking.uid}${
|
||||
booking.seatsReferences.length && isAttendee && seatReferenceUid ? `?seatReferenceUid=${seatReferenceUid}` : ""
|
||||
booking.seatsReferences.length && isAttendee && seatReferenceUid
|
||||
? `?seatReferenceUid=${seatReferenceUid}`
|
||||
: ""
|
||||
}`,
|
||||
disabled:
|
||||
(isBookingInPast && !booking.eventType.allowReschedulingPastBookings) || isDisabledRescheduling,
|
||||
|
||||
@@ -187,7 +187,7 @@ const useReroutingState = ({ isOpenDialog }: Pick<RerouteDialogProps, "isOpenDia
|
||||
const status = (() => {
|
||||
if (!value) return ReroutingStatusEnum.REROUTING_NOT_INITIATED;
|
||||
if (value.error) return ReroutingStatusEnum.REROUTING_FAILED;
|
||||
if (!!value.newBooking) return ReroutingStatusEnum.REROUTING_COMPLETE;
|
||||
if (value.newBooking) return ReroutingStatusEnum.REROUTING_COMPLETE;
|
||||
return ReroutingStatusEnum.REROUTING_IN_PROGRESS;
|
||||
})();
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ const UserProfile = ({ user }: UserProfileProps) => {
|
||||
const [imageSrc, setImageSrc] = useState<string>(user?.avatar || "");
|
||||
const utils = trpc.useUtils();
|
||||
const router = useRouter();
|
||||
const createEventType = trpc.viewer.eventTypes.heavy.create.useMutation();
|
||||
const createEventType = trpc.viewer.eventTypesHeavy.create.useMutation();
|
||||
const telemetry = useTelemetry();
|
||||
const [firstRender, setFirstRender] = useState(true);
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import { ThemeProvider } from "next-themes";
|
||||
import type { AppProps as NextAppProps } from "next/app";
|
||||
import type { ReadonlyURLSearchParams } from "next/navigation";
|
||||
import { usePathname, useSearchParams } from "next/navigation";
|
||||
import { NuqsAdapter } from "nuqs/adapters/next/app";
|
||||
|
||||
import DynamicPostHogProvider from "@calcom/features/ee/event-tracking/lib/posthog/providerDynamic";
|
||||
import { OrgBrandingProvider } from "@calcom/features/ee/organizations/context/provider";
|
||||
@@ -119,15 +120,17 @@ const AppProviders = (props: PageWrapperProps) => {
|
||||
nonce={props.nonce}
|
||||
isThemeSupported={isThemeSupported}
|
||||
isBookingPage={props.isBookingPage || isBookingPage}>
|
||||
<FeatureFlagsProvider>
|
||||
{props.isBookingPage || isBookingPage ? (
|
||||
<OrgBrandProvider>{props.children}</OrgBrandProvider>
|
||||
) : (
|
||||
<DynamicIntercomProvider>
|
||||
<NuqsAdapter>
|
||||
<FeatureFlagsProvider>
|
||||
{props.isBookingPage || isBookingPage ? (
|
||||
<OrgBrandProvider>{props.children}</OrgBrandProvider>
|
||||
</DynamicIntercomProvider>
|
||||
)}
|
||||
</FeatureFlagsProvider>
|
||||
) : (
|
||||
<DynamicIntercomProvider>
|
||||
<OrgBrandProvider>{props.children}</OrgBrandProvider>
|
||||
</DynamicIntercomProvider>
|
||||
)}
|
||||
</FeatureFlagsProvider>
|
||||
</NuqsAdapter>
|
||||
</CalcomThemeProvider>
|
||||
</TooltipProvider>
|
||||
</EventCollectionProvider>
|
||||
|
||||
@@ -8,6 +8,7 @@ import type { SSRConfig } from "next-i18next/dist/types/types";
|
||||
import { ThemeProvider } from "next-themes";
|
||||
import type { AppProps as NextAppProps, AppProps as NextJsAppProps } from "next/app";
|
||||
import dynamic from "next/dynamic";
|
||||
import { NuqsAdapter } from "nuqs/adapters/next/pages";
|
||||
import type { ParsedUrlQuery } from "querystring";
|
||||
import type { PropsWithChildren, ReactNode } from "react";
|
||||
import { useEffect } from "react";
|
||||
@@ -286,15 +287,17 @@ const AppProviders = (props: AppPropsWithChildren) => {
|
||||
isThemeSupported={props.Component.isThemeSupported}
|
||||
isBookingPage={props.Component.isBookingPage || isBookingPage}
|
||||
router={props.router}>
|
||||
<FeatureFlagsProvider>
|
||||
{_isBookingPage ? (
|
||||
<OrgBrandProvider>{props.children}</OrgBrandProvider>
|
||||
) : (
|
||||
<DynamicIntercomProvider>
|
||||
<NuqsAdapter>
|
||||
<FeatureFlagsProvider>
|
||||
{_isBookingPage ? (
|
||||
<OrgBrandProvider>{props.children}</OrgBrandProvider>
|
||||
</DynamicIntercomProvider>
|
||||
)}
|
||||
</FeatureFlagsProvider>
|
||||
) : (
|
||||
<DynamicIntercomProvider>
|
||||
<OrgBrandProvider>{props.children}</OrgBrandProvider>
|
||||
</DynamicIntercomProvider>
|
||||
)}
|
||||
</FeatureFlagsProvider>
|
||||
</NuqsAdapter>
|
||||
</CalcomThemeProvider>
|
||||
</TooltipProvider>
|
||||
</CustomI18nextProvider>
|
||||
|
||||
@@ -6,10 +6,10 @@ import type { LocationObject } from "@calcom/app-store/locations";
|
||||
import { isConferencing as isConferencingApp } from "@calcom/app-store/utils";
|
||||
import { getLocale } from "@calcom/features/auth/lib/getLocale";
|
||||
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
||||
import { UserRepository } from "@calcom/features/users/repositories/UserRepository";
|
||||
import { AppOnboardingSteps } from "@calcom/lib/apps/appOnboardingSteps";
|
||||
import { CAL_URL } from "@calcom/lib/constants";
|
||||
import { getPlaceholderAvatar } from "@calcom/lib/defaultAvatarImage";
|
||||
import { UserRepository } from "@calcom/features/users/repositories/UserRepository";
|
||||
import prisma from "@calcom/prisma";
|
||||
import type { Prisma } from "@calcom/prisma/client";
|
||||
import { eventTypeBookingFields } from "@calcom/prisma/zod-utils";
|
||||
@@ -281,7 +281,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
|
||||
id: user.id,
|
||||
name: user.name,
|
||||
avatarUrl: user.avatarUrl,
|
||||
alreadyInstalled: appInstalls.some((install) => !Boolean(install.teamId) && install.userId === user.id),
|
||||
alreadyInstalled: appInstalls.some((install) => !install.teamId && install.userId === user.id),
|
||||
};
|
||||
|
||||
const teamsWithIsAppInstalled = hasTeams
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { enrichFormWithMigrationData } from "@calcom/app-store/routing-forms/enrichFormWithMigrationData";
|
||||
import { getSerializableForm } from "@calcom/app-store/routing-forms/lib/getSerializableForm";
|
||||
import { MembershipRepository } from "@calcom/features/membership/repositories/MembershipRepository";
|
||||
import { Resource } from "@calcom/features/pbac/domain/types/permission-registry";
|
||||
import { getResourcePermissions } from "@calcom/features/pbac/lib/resource-permissions";
|
||||
import { MembershipRepository } from "@calcom/features/membership/repositories/MembershipRepository";
|
||||
import { MembershipRole } from "@calcom/prisma/enums";
|
||||
import type { AppGetServerSidePropsContext, AppPrisma, AppUser } from "@calcom/types/AppGetServerSideProps";
|
||||
|
||||
|
||||
@@ -8,12 +8,12 @@ import type { GetBookingType } from "@calcom/features/bookings/lib/get-booking";
|
||||
import { orgDomainConfig } from "@calcom/features/ee/organizations/lib/orgDomains";
|
||||
import { EventRepository } from "@calcom/features/eventtypes/repositories/EventRepository";
|
||||
import { FeaturesRepository } from "@calcom/features/flags/features.repository";
|
||||
import { HashedLinkService } from "@calcom/features/hashedLink/services/hashedLinkService";
|
||||
import {
|
||||
shouldHideBrandingForTeamEvent,
|
||||
shouldHideBrandingForUserEvent,
|
||||
} from "@calcom/features/profile/lib/hideBranding";
|
||||
import { UserRepository } from "@calcom/features/users/repositories/UserRepository";
|
||||
import { HashedLinkService } from "@calcom/features/hashedLink/lib/service/HashedLinkService";
|
||||
import slugify from "@calcom/lib/slugify";
|
||||
import prisma from "@calcom/prisma";
|
||||
import { RedirectType } from "@calcom/prisma/enums";
|
||||
@@ -82,9 +82,8 @@ async function getUserPageProps(context: GetServerSidePropsContext) {
|
||||
redirect: {
|
||||
permanent: false,
|
||||
// App Router doesn't have access to the current path directly, so we build it manually
|
||||
destination: `${redirectWithOriginAndSearchString.origin ?? ""}/d/${link}/${slug}${
|
||||
redirectWithOriginAndSearchString.searchString
|
||||
}`,
|
||||
destination: `${redirectWithOriginAndSearchString.origin ?? ""}/d/${link}/${slug}${redirectWithOriginAndSearchString.searchString
|
||||
}`,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@ import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
||||
import { getMultipleDurationValue } from "@calcom/features/bookings/lib/get-booking";
|
||||
import { getSlugOrRequestedSlug } from "@calcom/features/ee/organizations/lib/orgDomains";
|
||||
import { orgDomainConfig } from "@calcom/features/ee/organizations/lib/orgDomains";
|
||||
import { shouldHideBrandingForTeamEvent } from "@calcom/features/profile/lib/hideBranding";
|
||||
import { EventRepository } from "@calcom/features/eventtypes/repositories/EventRepository";
|
||||
import { shouldHideBrandingForTeamEvent } from "@calcom/features/profile/lib/hideBranding";
|
||||
import slugify from "@calcom/lib/slugify";
|
||||
import prisma from "@calcom/prisma";
|
||||
|
||||
|
||||
@@ -2,10 +2,10 @@ import type { NextApiRequest, NextApiResponse } from "next";
|
||||
import { z } from "zod";
|
||||
|
||||
import dayjs from "@calcom/dayjs";
|
||||
import { OrganizationRepository } from "@calcom/features/ee/organizations/repositories/OrganizationRepository";
|
||||
import { StripeBillingService } from "@calcom/features/ee/billing/stripe-billing-service";
|
||||
import { WEBAPP_URL } from "@calcom/lib/constants";
|
||||
import { IS_STRIPE_ENABLED } from "@calcom/lib/constants";
|
||||
import { OrganizationRepository } from "@calcom/features/ee/organizations/repositories/OrganizationRepository";
|
||||
import { prisma } from "@calcom/prisma";
|
||||
import { MembershipRole } from "@calcom/prisma/enums";
|
||||
import { CreationSource } from "@calcom/prisma/enums";
|
||||
|
||||
@@ -30,6 +30,7 @@ export const getServerSideProps = async (ctx: GetServerSidePropsContext) => {
|
||||
"email-verification"
|
||||
);
|
||||
const signupDisabled = await featuresRepository.checkIfFeatureIsEnabledGlobally("disable-signup");
|
||||
const onboardingV3Enabled = await featuresRepository.checkIfFeatureIsEnabledGlobally("onboarding-v3");
|
||||
|
||||
const token = z.string().optional().parse(ctx.query.token);
|
||||
const redirectUrlData = z
|
||||
@@ -62,6 +63,7 @@ export const getServerSideProps = async (ctx: GetServerSidePropsContext) => {
|
||||
isSAMLLoginEnabled,
|
||||
prepopulateFormValues: undefined,
|
||||
emailVerificationEnabled,
|
||||
onboardingV3Enabled,
|
||||
};
|
||||
|
||||
if ((process.env.NEXT_PUBLIC_DISABLE_SIGNUP === "true" && !token) || signupDisabled) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { GetServerSidePropsContext } from "next";
|
||||
|
||||
import { getBookerBaseUrlSync } from "@calcom/features/ee/organizations/lib/getBookerBaseUrlSync";
|
||||
import { orgDomainConfig } from "@calcom/features/ee/organizations/lib/orgDomains";
|
||||
import {
|
||||
getOrganizationSettings,
|
||||
@@ -9,7 +10,6 @@ import { getTeamWithMembers } from "@calcom/features/ee/teams/lib/queries";
|
||||
import { FeaturesRepository } from "@calcom/features/flags/features.repository";
|
||||
import { IS_CALCOM } from "@calcom/lib/constants";
|
||||
import { getUserAvatarUrl } from "@calcom/lib/getAvatarUrl";
|
||||
import { getBookerBaseUrlSync } from "@calcom/features/ee/organizations/lib/getBookerBaseUrlSync";
|
||||
import logger from "@calcom/lib/logger";
|
||||
import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML";
|
||||
import slugify from "@calcom/lib/slugify";
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
import type { RegularBookingCreateResult } from "@calcom/features/bookings/lib/dto/types";
|
||||
|
||||
export type BookingResponse = RegularBookingCreateResult;
|
||||
@@ -7,14 +7,14 @@ import {
|
||||
updateMeetingTokenIfExpired,
|
||||
} from "@calcom/app-store/dailyvideo/lib/VideoApiAdapter";
|
||||
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
||||
import { BookingRepository } from "@calcom/features/bookings/repositories/BookingRepository";
|
||||
import { OrganizationRepository } from "@calcom/features/ee/organizations/repositories/OrganizationRepository";
|
||||
import { EventTypeRepository } from "@calcom/features/eventtypes/repositories/eventTypeRepository";
|
||||
import { FeaturesRepository } from "@calcom/features/flags/features.repository";
|
||||
import { getCalVideoReference } from "@calcom/features/get-cal-video-reference";
|
||||
import { UserRepository } from "@calcom/features/users/repositories/UserRepository";
|
||||
import { CAL_VIDEO_MEETING_LINK_FOR_TESTING } from "@calcom/lib/constants";
|
||||
import { isENVDev } from "@calcom/lib/env";
|
||||
import { BookingRepository } from "@calcom/features/bookings/repositories/BookingRepository";
|
||||
import { OrganizationRepository } from "@calcom/features/ee/organizations/repositories/OrganizationRepository";
|
||||
import prisma from "@calcom/prisma";
|
||||
|
||||
const md = new MarkdownIt("default", { html: true, breaks: true, linkify: true });
|
||||
@@ -210,7 +210,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
|
||||
exp: epochTimeFourteenDaysAfter,
|
||||
});
|
||||
|
||||
const sessionUserId = !!session?.user?.impersonatedBy ? session.user.impersonatedBy.id : session?.user.id;
|
||||
const sessionUserId = session?.user?.impersonatedBy ? session.user.impersonatedBy.id : session?.user.id;
|
||||
const isOrganizer = await checkIfUserIsHost({
|
||||
booking: {
|
||||
eventTypeId: bookingObj.eventType?.id,
|
||||
@@ -238,7 +238,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
|
||||
videoReferencePassword,
|
||||
sessionUserId
|
||||
);
|
||||
if (!!meetingPassword) {
|
||||
if (meetingPassword) {
|
||||
bookingObj.references.forEach((bookRef) => {
|
||||
bookRef.meetingPassword = meetingPassword;
|
||||
});
|
||||
|
||||
@@ -8,6 +8,7 @@ import type { z } from "zod";
|
||||
|
||||
import checkForMultiplePaymentApps from "@calcom/app-store/_utils/payments/checkForMultiplePaymentApps";
|
||||
import useAddAppMutation from "@calcom/app-store/_utils/useAddAppMutation";
|
||||
import type { LocationObject } from "@calcom/app-store/locations";
|
||||
import type { EventTypeAppSettingsComponentProps, EventTypeModel } from "@calcom/app-store/types";
|
||||
import { eventTypeMetaDataSchemaWithTypedApps } from "@calcom/app-store/zod-utils";
|
||||
import type { LocationFormValues } from "@calcom/features/eventtypes/lib/types";
|
||||
@@ -15,7 +16,6 @@ import { AppOnboardingSteps } from "@calcom/lib/apps/appOnboardingSteps";
|
||||
import { getAppOnboardingUrl } from "@calcom/lib/apps/getAppOnboardingUrl";
|
||||
import { WEBAPP_URL } from "@calcom/lib/constants";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import type { LocationObject } from "@calcom/app-store/locations";
|
||||
import type { Team } from "@calcom/prisma/client";
|
||||
import type { eventTypeBookingFields } from "@calcom/prisma/zod-utils";
|
||||
import type { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
|
||||
@@ -177,7 +177,7 @@ const OnboardingPage = ({
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [eventTypeGroups]);
|
||||
|
||||
const updateMutation = trpc.viewer.eventTypes.heavy.update.useMutation({
|
||||
const updateMutation = trpc.viewer.eventTypesHeavy.update.useMutation({
|
||||
onSuccess: async (data) => {
|
||||
showToast(t("event_type_updated_successfully", { eventTypeTitle: data.eventType?.title }), "success");
|
||||
},
|
||||
@@ -309,21 +309,20 @@ const OnboardingPage = ({
|
||||
handleSetUpLater={handleSetUpLater}
|
||||
/>
|
||||
)}
|
||||
{currentStep === AppOnboardingSteps.CONFIGURE_STEP &&
|
||||
eventTypeGroups && (
|
||||
<ConfigureStepCard
|
||||
slug={appMetadata.slug}
|
||||
categories={appMetadata.categories}
|
||||
credentialId={credentialId}
|
||||
userName={userName}
|
||||
loading={updateMutation.isPending}
|
||||
formPortalRef={formPortalRef}
|
||||
setConfigureStep={setConfigureStep}
|
||||
eventTypeGroups={eventTypeGroups}
|
||||
handleSetUpLater={handleSetUpLater}
|
||||
isConferencing={isConferencing}
|
||||
/>
|
||||
)}
|
||||
{currentStep === AppOnboardingSteps.CONFIGURE_STEP && eventTypeGroups && (
|
||||
<ConfigureStepCard
|
||||
slug={appMetadata.slug}
|
||||
categories={appMetadata.categories}
|
||||
credentialId={credentialId}
|
||||
userName={userName}
|
||||
loading={updateMutation.isPending}
|
||||
formPortalRef={formPortalRef}
|
||||
setConfigureStep={setConfigureStep}
|
||||
eventTypeGroups={eventTypeGroups}
|
||||
handleSetUpLater={handleSetUpLater}
|
||||
isConferencing={isConferencing}
|
||||
/>
|
||||
)}
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user