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:
|
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`
|
- 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
|
- 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
|
## When working on workflow triggers or similar enum-based features in the Cal.com codebase
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ runs:
|
|||||||
with:
|
with:
|
||||||
path: ${{ inputs.path }}
|
path: ${{ inputs.path }}
|
||||||
key: ${{ runner.os }}-${{ env.cache-name }}-${{ inputs.path }}-${{ env.key-1 }}-${{ env.key-2 }}-${{ env.key-3 }}
|
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'
|
if: steps.cache-db.outputs.cache-hit != 'true'
|
||||||
shell: bash
|
shell: bash
|
||||||
- name: Postgres Dump Backup
|
- name: Postgres Dump Backup
|
||||||
|
|||||||
@@ -56,8 +56,8 @@ jobs:
|
|||||||
if: steps.cache-api-v2-build.outputs.cache-hit != 'true'
|
if: steps.cache-api-v2-build.outputs.cache-hit != 'true'
|
||||||
run: |
|
run: |
|
||||||
export NODE_OPTIONS="--max_old_space_size=8192"
|
export NODE_OPTIONS="--max_old_space_size=8192"
|
||||||
yarn workspace @calcom/api-v2 run generate-schemas
|
yarn workspace @calcom/api-v2 run generate-schemas &&
|
||||||
rm -rf apps/api/v2/node_modules
|
rm -rf apps/api/v2/node_modules &&
|
||||||
yarn install
|
yarn install &&
|
||||||
yarn workspace @calcom/api-v2 run build
|
yarn workspace @calcom/api-v2 run build
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ env:
|
|||||||
CALENDSO_ENCRYPTION_KEY: ${{ secrets.CI_CALENDSO_ENCRYPTION_KEY }}
|
CALENDSO_ENCRYPTION_KEY: ${{ secrets.CI_CALENDSO_ENCRYPTION_KEY }}
|
||||||
DAILY_API_KEY: ${{ secrets.CI_DAILY_API_KEY }}
|
DAILY_API_KEY: ${{ secrets.CI_DAILY_API_KEY }}
|
||||||
DATABASE_URL: ${{ secrets.CI_DATABASE_URL }}
|
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_EMAIL: ${{ secrets.E2E_TEST_APPLE_CALENDAR_EMAIL }}
|
||||||
E2E_TEST_APPLE_CALENDAR_PASSWORD: ${{ secrets.E2E_TEST_APPLE_CALENDAR_PASSWORD }}
|
E2E_TEST_APPLE_CALENDAR_PASSWORD: ${{ secrets.E2E_TEST_APPLE_CALENDAR_PASSWORD }}
|
||||||
E2E_TEST_MAILHOG_ENABLED: ${{ vars.E2E_TEST_MAILHOG_ENABLED }}
|
E2E_TEST_MAILHOG_ENABLED: ${{ vars.E2E_TEST_MAILHOG_ENABLED }}
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ env:
|
|||||||
CALENDSO_ENCRYPTION_KEY: ${{ secrets.CI_CALENDSO_ENCRYPTION_KEY }}
|
CALENDSO_ENCRYPTION_KEY: ${{ secrets.CI_CALENDSO_ENCRYPTION_KEY }}
|
||||||
DAILY_API_KEY: ${{ secrets.CI_DAILY_API_KEY }}
|
DAILY_API_KEY: ${{ secrets.CI_DAILY_API_KEY }}
|
||||||
DATABASE_URL: ${{ secrets.CI_DATABASE_URL }}
|
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_EMAIL: ${{ secrets.E2E_TEST_APPLE_CALENDAR_EMAIL }}
|
||||||
E2E_TEST_APPLE_CALENDAR_PASSWORD: ${{ secrets.E2E_TEST_APPLE_CALENDAR_PASSWORD }}
|
E2E_TEST_APPLE_CALENDAR_PASSWORD: ${{ secrets.E2E_TEST_APPLE_CALENDAR_PASSWORD }}
|
||||||
GOOGLE_API_CREDENTIALS: ${{ secrets.CI_GOOGLE_API_CREDENTIALS }}
|
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_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 }}
|
NEXT_PUBLIC_ORG_SELF_SERVE_ENABLED: ${{ vars.CI_NEXT_PUBLIC_ORG_SELF_SERVE_ENABLED }}
|
||||||
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Build Web App
|
name: Build Web App
|
||||||
|
|||||||
@@ -67,6 +67,9 @@ tsconfig.tsbuildinfo
|
|||||||
|
|
||||||
# Prisma generated files
|
# Prisma generated files
|
||||||
packages/prisma/client/*
|
packages/prisma/client/*
|
||||||
|
!packages/prisma/client/index.ts
|
||||||
|
apps/api/v2/generated/prisma/*
|
||||||
|
packages/prisma/generated/prisma/*
|
||||||
packages/prisma/zod/**/*.ts
|
packages/prisma/zod/**/*.ts
|
||||||
packages/prisma/enums
|
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>
|
</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 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)
|
- [Setup postgres DB with render](https://render.com/docs/databases)
|
||||||
|
|
||||||
1. Copy and paste your `DATABASE_URL` from `.env` to `.env.appStore`.
|
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.
|
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
|
### 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.
|
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 { addRequestId } from "./addRequestid";
|
||||||
import { captureErrors } from "./captureErrors";
|
import { captureErrors } from "./captureErrors";
|
||||||
import { captureUserId } from "./captureUserId";
|
import { captureUserId } from "./captureUserId";
|
||||||
import { checkIsInMaintenanceMode } from "./checkIsInMaintenanceMode";
|
|
||||||
import { extendRequest } from "./extendRequest";
|
import { extendRequest } from "./extendRequest";
|
||||||
import {
|
import {
|
||||||
HTTP_DELETE,
|
HTTP_DELETE,
|
||||||
@@ -26,7 +25,6 @@ const middleware = {
|
|||||||
HTTP_POST,
|
HTTP_POST,
|
||||||
HTTP_DELETE,
|
HTTP_DELETE,
|
||||||
addRequestId,
|
addRequestId,
|
||||||
checkIsInMaintenanceMode,
|
|
||||||
verifyApiKey,
|
verifyApiKey,
|
||||||
rateLimitApiKey,
|
rateLimitApiKey,
|
||||||
extendRequest,
|
extendRequest,
|
||||||
@@ -38,16 +36,14 @@ const middleware = {
|
|||||||
|
|
||||||
type Middleware = keyof typeof middleware;
|
type Middleware = keyof typeof middleware;
|
||||||
|
|
||||||
const middlewareOrder = [
|
const middlewareOrder: Middleware[] = [
|
||||||
// The order here, determines the order of execution
|
|
||||||
"checkIsInMaintenanceMode",
|
|
||||||
"extendRequest",
|
"extendRequest",
|
||||||
"captureErrors",
|
"captureErrors",
|
||||||
"verifyApiKey",
|
"verifyApiKey",
|
||||||
"rateLimitApiKey",
|
"rateLimitApiKey",
|
||||||
"addRequestId",
|
"addRequestId",
|
||||||
"captureUserId",
|
"captureUserId",
|
||||||
] as Middleware[]; // <-- Provide a list of middleware to call automatically
|
];
|
||||||
|
|
||||||
const withMiddleware = label(middleware, middlewareOrder);
|
const withMiddleware = label(middleware, middlewareOrder);
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ const getAllAdminMemberships = async (userId: number) => {
|
|||||||
where: {
|
where: {
|
||||||
userId: userId,
|
userId: userId,
|
||||||
accepted: true,
|
accepted: true,
|
||||||
OR: [{ role: MembershipRole.OWNER }, { role: MembershipRole.ADMIN }],
|
role: { in: [MembershipRole.OWNER, MembershipRole.ADMIN] },
|
||||||
},
|
},
|
||||||
select: {
|
select: {
|
||||||
team: {
|
team: {
|
||||||
@@ -56,16 +56,19 @@ export const getAccessibleUsers = async ({
|
|||||||
memberUserIds,
|
memberUserIds,
|
||||||
adminUserId,
|
adminUserId,
|
||||||
}: AccessibleUsersType): Promise<number[]> => {
|
}: 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({
|
const memberships = await prisma.membership.findMany({
|
||||||
where: {
|
where: {
|
||||||
team: {
|
team: {
|
||||||
isOrganization: true,
|
isOrganization: true,
|
||||||
},
|
},
|
||||||
accepted: true,
|
accepted: true,
|
||||||
OR: [
|
OR: orConditions,
|
||||||
{ userId: { in: memberUserIds } },
|
|
||||||
{ userId: adminUserId, role: { in: [MembershipRole.OWNER, MembershipRole.ADMIN] } },
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
select: {
|
select: {
|
||||||
userId: true,
|
userId: true,
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ async function authMiddleware(req: NextApiRequest) {
|
|||||||
if (isSystemWideAdmin) return;
|
if (isSystemWideAdmin) return;
|
||||||
// Find all references where the user has bookings
|
// Find all references where the user has bookings
|
||||||
const bookingReference = await prisma.bookingReference.findFirst({
|
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" });
|
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) {
|
export async function deleteHandler(req: NextApiRequest) {
|
||||||
const { query } = req;
|
const { query } = req;
|
||||||
const { id } = schemaQueryIdParseInt.parse(query);
|
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` };
|
return { message: `BookingReference with id: ${id} deleted` };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ import { schemaQueryIdParseInt } from "~/lib/validations/shared/queryIdTransform
|
|||||||
export async function getHandler(req: NextApiRequest) {
|
export async function getHandler(req: NextApiRequest) {
|
||||||
const { query } = req;
|
const { query } = req;
|
||||||
const { id } = schemaQueryIdParseInt.parse(query);
|
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) };
|
return { booking_reference: schemaBookingReferenceReadPublic.parse(booking_reference) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,13 +29,13 @@ import { schemaBookingReferenceReadPublic } from "~/lib/validations/booking-refe
|
|||||||
* 404:
|
* 404:
|
||||||
* description: No booking references were found
|
* description: No booking references were found
|
||||||
*/
|
*/
|
||||||
async function getHandler(req: NextApiRequest) {
|
export async function handler(req: NextApiRequest) {
|
||||||
const { userId, isSystemWideAdmin } = req;
|
const { userId, isSystemWideAdmin } = req;
|
||||||
const args: Prisma.BookingReferenceFindManyArgs = isSystemWideAdmin
|
const args: Prisma.BookingReferenceFindManyArgs = isSystemWideAdmin
|
||||||
? {}
|
? { where: { deleted: null } }
|
||||||
: { where: { booking: { userId } } };
|
: { where: { booking: { userId }, deleted: null } };
|
||||||
const data = await prisma.bookingReference.findMany(args);
|
const data = await prisma.bookingReference.findMany(args);
|
||||||
return { booking_references: data.map((br) => schemaBookingReferenceReadPublic.parse(br)) };
|
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 type { NextApiRequest, NextApiResponse } from "next";
|
||||||
|
|
||||||
import dayjs from "@calcom/dayjs";
|
import dayjs from "@calcom/dayjs";
|
||||||
import { isSupportedTimeZone } from "@calcom/lib/dayjs";
|
|
||||||
import { getAvailableSlotsService } from "@calcom/features/di/containers/AvailableSlots";
|
import { getAvailableSlotsService } from "@calcom/features/di/containers/AvailableSlots";
|
||||||
|
import { isSupportedTimeZone } from "@calcom/lib/dayjs";
|
||||||
import { HttpError } from "@calcom/lib/http-error";
|
import { HttpError } from "@calcom/lib/http-error";
|
||||||
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
|
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
|
||||||
import { createContext } from "@calcom/trpc/server/createContext";
|
import { createContext } from "@calcom/trpc/server/createContext";
|
||||||
@@ -48,7 +48,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
: availableSlots.slots;
|
: availableSlots.slots;
|
||||||
|
|
||||||
return { slots: slotsInProvidedTimeZone };
|
return { slots: slotsInProvidedTimeZone };
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
} catch (cause) {
|
} catch (cause) {
|
||||||
if (cause instanceof TRPCError) {
|
if (cause instanceof TRPCError) {
|
||||||
const statusCode = getHTTPStatusCodeFromError(cause);
|
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 { createMocks } from "node-mocks-http";
|
||||||
import { describe, expect, test, vi, afterEach } from "vitest";
|
import { describe, expect, test, vi, afterEach } from "vitest";
|
||||||
|
|
||||||
import { buildBooking } from "@calcom/lib/test/builder";
|
|
||||||
import {
|
import {
|
||||||
getRecordingsOfCalVideoByRoomName,
|
getRecordingsOfCalVideoByRoomName,
|
||||||
getDownloadLinkOfCalVideoByRecordingId,
|
getDownloadLinkOfCalVideoByRecordingId,
|
||||||
} from "@calcom/features/conferencing/lib/videoClient";
|
} from "@calcom/features/conferencing/lib/videoClient";
|
||||||
|
import { buildBooking } from "@calcom/lib/test/builder";
|
||||||
|
|
||||||
import { getAccessibleUsers } from "~/lib/utils/retrieveScopedAccessibleUsers";
|
import { getAccessibleUsers } from "~/lib/utils/retrieveScopedAccessibleUsers";
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import type { NextApiRequest, NextApiResponse } from "next";
|
|||||||
import { createMocks } from "node-mocks-http";
|
import { createMocks } from "node-mocks-http";
|
||||||
import { describe, expect, test, vi, afterEach } from "vitest";
|
import { describe, expect, test, vi, afterEach } from "vitest";
|
||||||
|
|
||||||
import { buildBooking } from "@calcom/lib/test/builder";
|
|
||||||
import { getAllTranscriptsAccessLinkFromRoomName } from "@calcom/features/conferencing/lib/videoClient";
|
import { getAllTranscriptsAccessLinkFromRoomName } from "@calcom/features/conferencing/lib/videoClient";
|
||||||
|
import { buildBooking } from "@calcom/lib/test/builder";
|
||||||
|
|
||||||
import { getAccessibleUsers } from "~/lib/utils/retrieveScopedAccessibleUsers";
|
import { getAccessibleUsers } from "~/lib/utils/retrieveScopedAccessibleUsers";
|
||||||
|
|
||||||
|
|||||||
@@ -42,4 +42,5 @@ lerna-debug.log*
|
|||||||
.env.*
|
.env.*
|
||||||
!.env.example
|
!.env.example
|
||||||
!.env.appStore.example
|
!.env.appStore.example
|
||||||
/client/*
|
/client/*
|
||||||
|
/generated/prisma/*
|
||||||
|
|||||||
@@ -22,6 +22,10 @@ const loadConfig = (): AppConfig => {
|
|||||||
db: {
|
db: {
|
||||||
readUrl: getEnv("DATABASE_READ_URL"),
|
readUrl: getEnv("DATABASE_READ_URL"),
|
||||||
writeUrl: getEnv("DATABASE_WRITE_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"),
|
redisUrl: getEnv("REDIS_URL"),
|
||||||
},
|
},
|
||||||
next: {
|
next: {
|
||||||
|
|||||||
@@ -13,6 +13,10 @@ export type AppConfig = {
|
|||||||
db: {
|
db: {
|
||||||
readUrl: string;
|
readUrl: string;
|
||||||
writeUrl: string;
|
writeUrl: string;
|
||||||
|
readPoolMax: number;
|
||||||
|
writePoolMax: number;
|
||||||
|
workerReadPoolMax: number;
|
||||||
|
workerWritePoolMax: number;
|
||||||
redisUrl: string;
|
redisUrl: string;
|
||||||
};
|
};
|
||||||
next: {
|
next: {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export class BookingReferencesRepository_2024_08_13 {
|
|||||||
constructor(private readonly dbRead: PrismaReadService) {}
|
constructor(private readonly dbRead: PrismaReadService) {}
|
||||||
|
|
||||||
async getBookingReferences(bookingId: number, filter?: BookingReferencesFilterInput_2024_08_13) {
|
async getBookingReferences(bookingId: number, filter?: BookingReferencesFilterInput_2024_08_13) {
|
||||||
const whereClause: Prisma.BookingReferenceWhereInput = { bookingId };
|
const whereClause: Prisma.BookingReferenceWhereInput = { bookingId, deleted: null };
|
||||||
|
|
||||||
if (filter?.type) {
|
if (filter?.type) {
|
||||||
whereClause.type = filter.type;
|
whereClause.type = filter.type;
|
||||||
@@ -30,6 +30,7 @@ export class BookingReferencesRepository_2024_08_13 {
|
|||||||
return this.dbRead.prisma.bookingReference.findFirst({
|
return this.dbRead.prisma.bookingReference.findFirst({
|
||||||
where: {
|
where: {
|
||||||
uid: eventUid,
|
uid: eventUid,
|
||||||
|
deleted: null,
|
||||||
},
|
},
|
||||||
include: {
|
include: {
|
||||||
credential: true,
|
credential: true,
|
||||||
|
|||||||
+3
-1
@@ -39,9 +39,11 @@ export class EventTypesService_2024_06_14 {
|
|||||||
}
|
}
|
||||||
await this.checkCanCreateEventType(user.id, body);
|
await this.checkCanCreateEventType(user.id, body);
|
||||||
const eventTypeUser = await this.getUserToCreateEvent(user);
|
const eventTypeUser = await this.getUserToCreateEvent(user);
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
const { destinationCalendar: _destinationCalendar, ...rest } = body;
|
const { destinationCalendar: _destinationCalendar, ...rest } = body;
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
const { eventType: eventTypeCreated } = await createEventType({
|
const { eventType: eventTypeCreated } = await createEventType({
|
||||||
input: rest,
|
input: rest,
|
||||||
ctx: {
|
ctx: {
|
||||||
|
|||||||
@@ -34,6 +34,10 @@ export type Environment = {
|
|||||||
IS_TEAM_BILLING_ENABLED: boolean;
|
IS_TEAM_BILLING_ENABLED: boolean;
|
||||||
// Used to enable/disable the rewrite of /api/v2 to /v2, active by default.
|
// Used to enable/disable the rewrite of /api/v2 to /v2, active by default.
|
||||||
REWRITE_API_V2_PREFIX: string;
|
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] => {
|
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 { NoSlotsNotificationService } from "@/lib/services/no-slots-notification.service";
|
||||||
import { QualifiedHostsService } from "@/lib/services/qualified-hosts.service";
|
import { QualifiedHostsService } from "@/lib/services/qualified-hosts.service";
|
||||||
import { UserAvailabilityService } from "@/lib/services/user-availability.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 { RedisService } from "@/modules/redis/redis.service";
|
||||||
import { Module } from "@nestjs/common";
|
import { Module } from "@nestjs/common";
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [PrismaModule],
|
imports: [PrismaWorkerModule],
|
||||||
providers: [
|
providers: [
|
||||||
PrismaOOORepository,
|
PrismaOOORepository,
|
||||||
PrismaScheduleRepository,
|
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 { CacheService } from "@/lib/services/cache.service";
|
||||||
import { CheckBookingAndDurationLimitsService } from "@/lib/services/check-booking-and-duration-limits.service";
|
import { CheckBookingAndDurationLimitsService } from "@/lib/services/check-booking-and-duration-limits.service";
|
||||||
import { CheckBookingLimitsService } from "@/lib/services/check-booking-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 { LuckyUserService } from "@/lib/services/lucky-user.service";
|
||||||
import { RegularBookingService } from "@/lib/services/regular-booking.service";
|
import { RegularBookingService } from "@/lib/services/regular-booking.service";
|
||||||
import { PrismaModule } from "@/modules/prisma/prisma.module";
|
import { PrismaModule } from "@/modules/prisma/prisma.module";
|
||||||
@@ -24,9 +25,10 @@ import { Module } from "@nestjs/common";
|
|||||||
CacheService,
|
CacheService,
|
||||||
CheckBookingAndDurationLimitsService,
|
CheckBookingAndDurationLimitsService,
|
||||||
CheckBookingLimitsService,
|
CheckBookingLimitsService,
|
||||||
|
HashedLinkService,
|
||||||
LuckyUserService,
|
LuckyUserService,
|
||||||
RegularBookingService,
|
RegularBookingService,
|
||||||
],
|
],
|
||||||
exports: [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 { 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 { PrismaUserRepository } from "@/lib/repositories/prisma-user.repository";
|
||||||
import { CacheService } from "@/lib/services/cache.service";
|
import { CacheService } from "@/lib/services/cache.service";
|
||||||
import { CheckBookingAndDurationLimitsService } from "@/lib/services/check-booking-and-duration-limits.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 { LuckyUserService } from "@/lib/services/lucky-user.service";
|
||||||
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
|
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
|
||||||
import { Injectable } from "@nestjs/common";
|
import { Injectable } from "@nestjs/common";
|
||||||
@@ -21,26 +17,18 @@ export class RegularBookingService extends BaseRegularBookingService {
|
|||||||
checkBookingAndDurationLimitsService: CheckBookingAndDurationLimitsService,
|
checkBookingAndDurationLimitsService: CheckBookingAndDurationLimitsService,
|
||||||
prismaWriteService: PrismaWriteService,
|
prismaWriteService: PrismaWriteService,
|
||||||
bookingRepository: PrismaBookingRepository,
|
bookingRepository: PrismaBookingRepository,
|
||||||
featuresRepository: PrismaFeaturesRepository,
|
hashedLinkService: HashedLinkService,
|
||||||
checkBookingLimitsService: CheckBookingLimitsService,
|
|
||||||
luckyUserService: LuckyUserService,
|
luckyUserService: LuckyUserService,
|
||||||
hostRepository: PrismaHostRepository,
|
userRepository: PrismaUserRepository
|
||||||
oooRepository: PrismaOOORepository,
|
|
||||||
userRepository: PrismaUserRepository,
|
|
||||||
attributeRepository: PrismaAttributeRepository
|
|
||||||
) {
|
) {
|
||||||
super({
|
super({
|
||||||
cacheService,
|
cacheService,
|
||||||
checkBookingAndDurationLimitsService,
|
checkBookingAndDurationLimitsService,
|
||||||
prismaClient: prismaWriteService.prisma as unknown as PrismaClient,
|
prismaClient: prismaWriteService.prisma as unknown as PrismaClient,
|
||||||
bookingRepository,
|
bookingRepository,
|
||||||
featuresRepository,
|
hashedLinkService,
|
||||||
checkBookingLimitsService,
|
|
||||||
luckyUserService,
|
luckyUserService,
|
||||||
hostRepository,
|
|
||||||
oooRepository,
|
|
||||||
userRepository,
|
userRepository,
|
||||||
attributeRepository,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { PlatformPlan } from "@/modules/billing/types";
|
import { PlatformPlan } from "@/modules/billing/types";
|
||||||
import type { StripeService } from "@/modules/stripe/stripe.service";
|
import type { StripeService } from "@/modules/stripe/stripe.service";
|
||||||
import Stripe from "stripe";
|
import Stripe from "stripe";
|
||||||
|
|
||||||
import { PlatformBilling, Team } from "@calcom/prisma/client";
|
import { PlatformBilling, Team } from "@calcom/prisma/client";
|
||||||
|
|
||||||
export type BillingData = {
|
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) {
|
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 { Injectable } from "@nestjs/common";
|
||||||
import { intersectionBy } from "lodash";
|
import { intersectionBy } from "lodash";
|
||||||
|
|
||||||
|
import type { Membership } from "@calcom/prisma/client";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MembershipsService {
|
export class MembershipsService {
|
||||||
constructor(private readonly membershipsRepository: MembershipsRepository) {}
|
constructor(private readonly membershipsRepository: MembershipsRepository) {}
|
||||||
@@ -16,8 +18,8 @@ export class MembershipsService {
|
|||||||
const secondUserMemberships = await this.membershipsRepository.findUserMemberships(secondUserId);
|
const secondUserMemberships = await this.membershipsRepository.findUserMemberships(secondUserId);
|
||||||
|
|
||||||
return intersectionBy(
|
return intersectionBy(
|
||||||
firstUserMemberships.filter((m) => m.accepted),
|
firstUserMemberships.filter((m: Membership) => m.accepted),
|
||||||
secondUserMemberships.filter((m) => m.accepted),
|
secondUserMemberships.filter((m: Membership) => m.accepted),
|
||||||
"teamId"
|
"teamId"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-14
@@ -434,20 +434,19 @@ describe("Organizations Event Types Endpoints", () => {
|
|||||||
|
|
||||||
const responseTeamEvent = responseBody.data.find((event) => event.teamId === team.id);
|
const responseTeamEvent = responseBody.data.find((event) => event.teamId === team.id);
|
||||||
expect(responseTeamEvent).toBeDefined();
|
expect(responseTeamEvent).toBeDefined();
|
||||||
expect(responseTeamEvent?.hosts).toEqual([
|
expect(responseTeamEvent?.hosts?.find((host) => host.userId === teammate1.id)).toEqual({
|
||||||
{
|
userId: teammate1.id,
|
||||||
userId: teammate1.id,
|
name: teammate1.name,
|
||||||
name: teammate1.name,
|
username: teammate1.username,
|
||||||
username: teammate1.username,
|
avatarUrl: teammate1.avatarUrl,
|
||||||
avatarUrl: teammate1.avatarUrl,
|
});
|
||||||
},
|
|
||||||
{
|
expect(responseTeamEvent?.hosts?.find((host) => host.userId === teammate2.id)).toEqual({
|
||||||
userId: teammate2.id,
|
userId: teammate2.id,
|
||||||
name: teammate2.name,
|
name: teammate2.name,
|
||||||
username: teammate2.username,
|
username: teammate2.username,
|
||||||
avatarUrl: teammate2.avatarUrl,
|
avatarUrl: teammate2.avatarUrl,
|
||||||
},
|
});
|
||||||
]);
|
|
||||||
|
|
||||||
if (!responseTeamEvent) {
|
if (!responseTeamEvent) {
|
||||||
throw new Error("Team event not found");
|
throw new Error("Team event not found");
|
||||||
|
|||||||
-3
@@ -122,7 +122,6 @@ describe("Organizations Teams Invite Endpoints", () => {
|
|||||||
.expect(404);
|
.expect(404);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
afterAll(async () => {
|
afterAll(async () => {
|
||||||
await userRepositoryFixture.deleteByEmail(user.email);
|
await userRepositoryFixture.deleteByEmail(user.email);
|
||||||
await organizationsRepositoryFixture.delete(org.id);
|
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 { 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 { Roles } from "@/modules/auth/decorators/roles/roles.decorator";
|
||||||
import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard";
|
import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard";
|
||||||
import { PlatformPlanGuard } from "@/modules/auth/guards/billing/platform-plan.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 { ApiHeader, ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
|
||||||
|
|
||||||
import { SUCCESS_STATUS } from "@calcom/platform-constants";
|
import { SUCCESS_STATUS } from "@calcom/platform-constants";
|
||||||
|
|
||||||
import { TeamService } from "@calcom/platform-libraries";
|
import { TeamService } from "@calcom/platform-libraries";
|
||||||
|
|
||||||
|
|
||||||
import { CreateInviteOutputDto } from "./outputs/invite.output";
|
import { CreateInviteOutputDto } from "./outputs/invite.output";
|
||||||
|
|
||||||
@Controller({
|
@Controller({
|
||||||
@@ -39,5 +41,3 @@ export class OrganizationsTeamsInviteController {
|
|||||||
return { status: SUCCESS_STATUS, data: result };
|
return { status: SUCCESS_STATUS, data: result };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ export class InviteDataDto {
|
|||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description:
|
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.",
|
"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;
|
inviteLink!: string;
|
||||||
}
|
}
|
||||||
@@ -23,5 +24,3 @@ export class CreateInviteOutputDto {
|
|||||||
@ApiProperty({ type: InviteDataDto })
|
@ApiProperty({ type: InviteDataDto })
|
||||||
data!: InviteDataDto;
|
data!: InviteDataDto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+17
-2
@@ -404,7 +404,7 @@ describe("OrganizationsTeamsWorkflowsController (E2E)", () => {
|
|||||||
invalidWorkflow.steps = [
|
invalidWorkflow.steps = [
|
||||||
{
|
{
|
||||||
stepNumber: 1,
|
stepNumber: 1,
|
||||||
action: "sms_number",
|
action: "cal_ai_phone_call",
|
||||||
recipient: PHONE_NUMBER,
|
recipient: PHONE_NUMBER,
|
||||||
template: REMINDER,
|
template: REMINDER,
|
||||||
verifiedPhoneId: verifiedPhoneId,
|
verifiedPhoneId: verifiedPhoneId,
|
||||||
@@ -439,6 +439,18 @@ describe("OrganizationsTeamsWorkflowsController (E2E)", () => {
|
|||||||
html: "<p>Reminder for your event {EVENT_NAME}.</p>",
|
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())
|
return request(app.getHttpServer())
|
||||||
.post(`${basePath}/routing-form`)
|
.post(`${basePath}/routing-form`)
|
||||||
@@ -459,12 +471,15 @@ describe("OrganizationsTeamsWorkflowsController (E2E)", () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
expect(responseBody.data.trigger.type).toEqual(sampleCreateWorkflowRoutingFormDto.trigger.type);
|
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)?.id).toBeDefined();
|
||||||
expect(responseBody.data.steps.find((step) => step.stepNumber === 1)?.sender).toEqual(
|
expect(responseBody.data.steps.find((step) => step.stepNumber === 1)?.sender).toEqual(
|
||||||
"CalcomE2EStep1"
|
"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;
|
const trigger = sampleCreateWorkflowRoutingFormDto.trigger as OnFormSubmittedTriggerDto;
|
||||||
expect(responseBody.data.trigger?.type).toEqual(trigger.type);
|
expect(responseBody.data.trigger?.type).toEqual(trigger.type);
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
|
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
|
||||||
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
|
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
|
||||||
import { Injectable } from "@nestjs/common";
|
import { Injectable } from "@nestjs/common";
|
||||||
import type { Prisma } from "@calcom/prisma/client";
|
|
||||||
|
import type { Prisma, AttributeToUser, Membership, User, Profile } from "@calcom/prisma/client";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class OrganizationsUsersRepository {
|
export class OrganizationsUsersRepository {
|
||||||
@@ -60,7 +61,11 @@ export class OrganizationsUsersRepository {
|
|||||||
skip,
|
skip,
|
||||||
take,
|
take,
|
||||||
});
|
});
|
||||||
return attributeToUsersWithProfile.map((attributeToUser) => attributeToUser.member.user);
|
return attributeToUsersWithProfile.map(
|
||||||
|
(
|
||||||
|
attributeToUser: AttributeToUser & { member: Membership & { user: User & { profiles: Profile[] } } }
|
||||||
|
) => attributeToUser.member.user
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getOrganizationUsersByEmails(
|
async getOrganizationUsersByEmails(
|
||||||
|
|||||||
@@ -1,36 +1,79 @@
|
|||||||
import type { OnModuleDestroy, OnModuleInit } from "@nestjs/common";
|
import type { OnModuleDestroy, OnModuleInit } from "@nestjs/common";
|
||||||
import { Injectable, Logger } from "@nestjs/common";
|
import { Injectable, Logger } from "@nestjs/common";
|
||||||
import { ConfigService } from "@nestjs/config";
|
import { ConfigService } from "@nestjs/config";
|
||||||
|
import { PrismaPg } from "@prisma/adapter-pg";
|
||||||
|
import { Pool } from "pg";
|
||||||
|
|
||||||
import { PrismaClient } from "@calcom/prisma/client";
|
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()
|
@Injectable()
|
||||||
export class PrismaReadService implements OnModuleInit, OnModuleDestroy {
|
export class PrismaReadService implements OnModuleInit, OnModuleDestroy {
|
||||||
private logger = new Logger("PrismaReadService");
|
private logger = new Logger("PrismaReadService");
|
||||||
|
|
||||||
public prisma: PrismaClient;
|
public prisma!: PrismaClient;
|
||||||
|
private pool!: Pool;
|
||||||
|
private options!: PrismaServiceOptions;
|
||||||
|
|
||||||
constructor(readonly configService: ConfigService) {
|
constructor(private configService?: ConfigService) {
|
||||||
const dbUrl = configService.get("db.readUrl", { infer: true });
|
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({
|
this.setOptions({
|
||||||
datasources: {
|
readUrl,
|
||||||
db: {
|
maxReadConnections: poolMax,
|
||||||
url: dbUrl,
|
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() {
|
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() {
|
async onModuleDestroy() {
|
||||||
try {
|
try {
|
||||||
await this.prisma.$disconnect();
|
if (this.prisma) await this.prisma.$disconnect();
|
||||||
|
if (this.pool) await this.pool.end();
|
||||||
} catch (error) {
|
} 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 { OnModuleDestroy, OnModuleInit, Injectable, Logger } from "@nestjs/common";
|
||||||
import { Injectable, Logger } from "@nestjs/common";
|
|
||||||
import { ConfigService } from "@nestjs/config";
|
import { ConfigService } from "@nestjs/config";
|
||||||
|
import { PrismaPg } from "@prisma/adapter-pg";
|
||||||
|
import { Pool } from "pg";
|
||||||
|
|
||||||
import { PrismaClient } from "@calcom/prisma/client";
|
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()
|
@Injectable()
|
||||||
export class PrismaWriteService implements OnModuleInit, OnModuleDestroy {
|
export class PrismaWriteService implements OnModuleInit, OnModuleDestroy {
|
||||||
private logger = new Logger("PrismaWriteService");
|
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) {
|
this.setOptions({
|
||||||
const dbUrl = configService.get("db.writeUrl", { infer: true });
|
writeUrl,
|
||||||
|
maxWriteConnections: poolMax,
|
||||||
|
e2e,
|
||||||
|
type: "main",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.prisma = new PrismaClient({
|
setOptions(options: PrismaServiceOptions) {
|
||||||
datasources: {
|
this.options = options;
|
||||||
db: {
|
|
||||||
url: dbUrl,
|
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() {
|
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() {
|
async onModuleDestroy() {
|
||||||
try {
|
try {
|
||||||
await this.prisma.$disconnect();
|
if (this.prisma) await this.prisma.$disconnect();
|
||||||
|
if (this.pool) await this.pool.end();
|
||||||
} catch (error) {
|
} 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 { PrismaReadService } from "@/modules/prisma/prisma-read.service";
|
||||||
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
|
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
|
||||||
import { Module } from "@nestjs/common";
|
import { Module } from "@nestjs/common";
|
||||||
|
import { ConfigModule, ConfigService } from "@nestjs/config";
|
||||||
|
|
||||||
@Module({
|
@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],
|
exports: [PrismaReadService, PrismaWriteService],
|
||||||
})
|
})
|
||||||
export class PrismaModule {}
|
export class PrismaModule {}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// Needed if ConfigService is used anywhere
|
// Needed if ConfigService is used anywhere
|
||||||
import appConfig from "@/config/app";
|
import appConfig from "@/config/app";
|
||||||
import { AvailableSlotsModule } from "@/lib/modules/available-slots.module";
|
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 { Module, Logger } from "@nestjs/common";
|
||||||
import { ConfigModule } from "@nestjs/config";
|
import { ConfigModule } from "@nestjs/config";
|
||||||
import { SentryModule } from "@sentry/nestjs/setup";
|
import { SentryModule } from "@sentry/nestjs/setup";
|
||||||
@@ -15,7 +15,7 @@ import { SentryModule } from "@sentry/nestjs/setup";
|
|||||||
isGlobal: true,
|
isGlobal: true,
|
||||||
load: [appConfig],
|
load: [appConfig],
|
||||||
}),
|
}),
|
||||||
PrismaModule,
|
PrismaWorkerModule,
|
||||||
AvailableSlotsModule,
|
AvailableSlotsModule,
|
||||||
],
|
],
|
||||||
providers: [Logger],
|
providers: [Logger],
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ export class StripeService {
|
|||||||
userId
|
userId
|
||||||
);
|
);
|
||||||
|
|
||||||
const credentialIdsToDelete = existingCredentials.map((item) => item.id);
|
const credentialIdsToDelete = existingCredentials.map((item: Credential) => item.id);
|
||||||
if (credentialIdsToDelete.length > 0) {
|
if (credentialIdsToDelete.length > 0) {
|
||||||
await this.appsRepository.deleteAppCredentials(credentialIdsToDelete, userId);
|
await this.appsRepository.deleteAppCredentials(credentialIdsToDelete, userId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export const MembershipUserSelect: Prisma.UserSelect = {
|
|||||||
name: true,
|
name: true,
|
||||||
metadata: true,
|
metadata: true,
|
||||||
bio: true,
|
bio: true,
|
||||||
};
|
} satisfies Prisma.UserSelect;
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class TeamsMembershipsRepository {
|
export class TeamsMembershipsRepository {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
|
|||||||
import { Injectable, NotFoundException } from "@nestjs/common";
|
import { Injectable, NotFoundException } from "@nestjs/common";
|
||||||
|
|
||||||
import { teamMetadataSchema } from "@calcom/platform-libraries";
|
import { teamMetadataSchema } from "@calcom/platform-libraries";
|
||||||
import { Prisma } from "@calcom/prisma/client";
|
import type { Membership, Prisma } from "@calcom/prisma/client";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class TeamsRepository {
|
export class TeamsRepository {
|
||||||
@@ -41,7 +41,7 @@ export class TeamsRepository {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return teamMembers.map((member) => member.userId);
|
return teamMembers.map((member: Membership) => member.userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getTeamManagedUsersIds(teamId: number) {
|
async getTeamManagedUsersIds(teamId: number) {
|
||||||
@@ -57,7 +57,7 @@ export class TeamsRepository {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return teamMembers.map((member) => member.userId);
|
return teamMembers.map((member: Membership) => member.userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getTeamsUserIsMemberOf(userId: number) {
|
async getTeamsUserIsMemberOf(userId: number) {
|
||||||
|
|||||||
@@ -289,7 +289,7 @@ export class UsersRepository {
|
|||||||
user: true,
|
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) {
|
async setDefaultConferencingApp(userId: number, appSlug?: string, appLink?: string) {
|
||||||
|
|||||||
@@ -7,8 +7,12 @@ import {
|
|||||||
EMAIL_ADDRESS,
|
EMAIL_ADDRESS,
|
||||||
EMAIL_ATTENDEE,
|
EMAIL_ATTENDEE,
|
||||||
FORM_ALLOWED_STEP_ACTIONS,
|
FORM_ALLOWED_STEP_ACTIONS,
|
||||||
|
SMS_NUMBER,
|
||||||
WorkflowEmailAddressStepDto,
|
WorkflowEmailAddressStepDto,
|
||||||
WorkflowEmailAttendeeStepDto,
|
WorkflowEmailAttendeeStepDto,
|
||||||
|
WorkflowPhoneNumberStepDto,
|
||||||
|
WorkflowPhoneAttendeeStepDto,
|
||||||
|
SMS_ATTENDEE,
|
||||||
} from "./workflow-step.input";
|
} from "./workflow-step.input";
|
||||||
import {
|
import {
|
||||||
RoutingFormWorkflowTriggerDto,
|
RoutingFormWorkflowTriggerDto,
|
||||||
@@ -85,6 +89,8 @@ export class CreateFormWorkflowDto {
|
|||||||
oneOf: [
|
oneOf: [
|
||||||
{ $ref: getSchemaPath(WorkflowEmailAddressStepDto) },
|
{ $ref: getSchemaPath(WorkflowEmailAddressStepDto) },
|
||||||
{ $ref: getSchemaPath(WorkflowEmailAttendeeStepDto) },
|
{ $ref: getSchemaPath(WorkflowEmailAttendeeStepDto) },
|
||||||
|
{ $ref: getSchemaPath(WorkflowPhoneAttendeeStepDto) },
|
||||||
|
{ $ref: getSchemaPath(WorkflowPhoneNumberStepDto) },
|
||||||
],
|
],
|
||||||
type: "array",
|
type: "array",
|
||||||
})
|
})
|
||||||
@@ -99,8 +105,15 @@ export class CreateFormWorkflowDto {
|
|||||||
subTypes: [
|
subTypes: [
|
||||||
{ value: WorkflowEmailAddressStepDto, name: EMAIL_ADDRESS },
|
{ value: WorkflowEmailAddressStepDto, name: EMAIL_ADDRESS },
|
||||||
{ value: WorkflowEmailAttendeeStepDto, name: EMAIL_ATTENDEE },
|
{ 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_ADDRESS,
|
||||||
EMAIL_ATTENDEE,
|
EMAIL_ATTENDEE,
|
||||||
FORM_ALLOWED_STEP_ACTIONS,
|
FORM_ALLOWED_STEP_ACTIONS,
|
||||||
|
SMS_ATTENDEE,
|
||||||
|
SMS_NUMBER,
|
||||||
UpdateEmailAddressWorkflowStepDto,
|
UpdateEmailAddressWorkflowStepDto,
|
||||||
UpdateEmailAttendeeWorkflowStepDto,
|
UpdateEmailAttendeeWorkflowStepDto,
|
||||||
UpdateEmailHostWorkflowStepDto,
|
UpdateEmailHostWorkflowStepDto,
|
||||||
@@ -75,6 +77,8 @@ export class UpdateFormWorkflowDto {
|
|||||||
oneOf: [
|
oneOf: [
|
||||||
{ $ref: getSchemaPath(UpdateEmailAddressWorkflowStepDto) },
|
{ $ref: getSchemaPath(UpdateEmailAddressWorkflowStepDto) },
|
||||||
{ $ref: getSchemaPath(UpdateEmailAttendeeWorkflowStepDto) },
|
{ $ref: getSchemaPath(UpdateEmailAttendeeWorkflowStepDto) },
|
||||||
|
{ $ref: getSchemaPath(UpdatePhoneAttendeeWorkflowStepDto) },
|
||||||
|
{ $ref: getSchemaPath(UpdatePhoneNumberWorkflowStepDto) },
|
||||||
],
|
],
|
||||||
type: "array",
|
type: "array",
|
||||||
})
|
})
|
||||||
@@ -90,8 +94,15 @@ export class UpdateFormWorkflowDto {
|
|||||||
subTypes: [
|
subTypes: [
|
||||||
{ value: UpdateEmailAddressWorkflowStepDto, name: EMAIL_ADDRESS },
|
{ value: UpdateEmailAddressWorkflowStepDto, name: EMAIL_ADDRESS },
|
||||||
{ value: UpdateEmailAttendeeWorkflowStepDto, name: EMAIL_ATTENDEE },
|
{ 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,
|
CAL_AI_PHONE_CALL,
|
||||||
] as const;
|
] 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 = {
|
export const STEP_ACTIONS_TO_ENUM = {
|
||||||
[EMAIL_HOST]: WorkflowActions.EMAIL_HOST,
|
[EMAIL_HOST]: WorkflowActions.EMAIL_HOST,
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
"jsx": "react-jsx",
|
"jsx": "react-jsx",
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["./src/*"],
|
"@/*": ["./src/*"],
|
||||||
"@prisma/client/*": ["./client/*"],
|
"@calcom/prisma/client": ["../../../packages/prisma/generated/prisma/client"],
|
||||||
"@calcom/prisma/enums": ["../../../packages/prisma/enums"],
|
"@calcom/prisma/enums": ["../../../packages/prisma/enums"],
|
||||||
"@calcom/platform-constants": ["../../../packages/platform/constants/index.ts"],
|
"@calcom/platform-constants": ["../../../packages/platform/constants/index.ts"],
|
||||||
"@calcom/platform-types": ["../../../packages/platform/types/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 { eventTypeMetaDataSchemaWithTypedApps } from "@calcom/app-store/zod-utils";
|
||||||
import { getTeamData } from "@calcom/features/ee/teams/lib/getTeamData";
|
import { getTeamData } from "@calcom/features/ee/teams/lib/getTeamData";
|
||||||
|
import { TeamRepository } from "@calcom/features/ee/teams/repositories/TeamRepository";
|
||||||
import {
|
import {
|
||||||
getEventTypeHosts,
|
getEventTypeHosts,
|
||||||
getProfileFromEvent,
|
getProfileFromEvent,
|
||||||
@@ -11,10 +12,9 @@ import {
|
|||||||
} from "@calcom/features/eventtypes/lib/getPublicEvent";
|
} from "@calcom/features/eventtypes/lib/getPublicEvent";
|
||||||
import { getTeamEventType } from "@calcom/features/eventtypes/lib/getTeamEventType";
|
import { getTeamEventType } from "@calcom/features/eventtypes/lib/getTeamEventType";
|
||||||
import { FeaturesRepository } from "@calcom/features/flags/features.repository";
|
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 { NEXTJS_CACHE_TTL } from "@calcom/lib/constants";
|
||||||
import { getPlaceholderAvatar } from "@calcom/lib/defaultAvatarImage";
|
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 { prisma } from "@calcom/prisma";
|
||||||
import type { Prisma } from "@calcom/prisma/client";
|
import type { Prisma } from "@calcom/prisma/client";
|
||||||
import type { SchedulingType } from "@calcom/prisma/enums";
|
import type { SchedulingType } from "@calcom/prisma/enums";
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import { MembershipRole } from "@calcom/prisma/enums";
|
|||||||
import { buildLegacyRequest } from "@lib/buildLegacyCtx";
|
import { buildLegacyRequest } from "@lib/buildLegacyCtx";
|
||||||
|
|
||||||
import { validStatuses } from "~/bookings/lib/validStatuses";
|
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({
|
const querySchema = z.object({
|
||||||
status: z.enum(validStatuses),
|
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 session = await getServerSession({ req: buildLegacyRequest(await headers(), await cookies()) });
|
||||||
const searchParams = await _searchParams;
|
const searchParams = await _searchParams;
|
||||||
const token = Array.isArray(searchParams?.token) ? searchParams.token[0] : searchParams?.token;
|
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)
|
||||||
const callbackUrl = token ? `/teams?token=${encodeURIComponent(token)}${autoAccept ? `&autoAccept=${encodeURIComponent(autoAccept)}` : ''}` : null;
|
? searchParams.autoAccept[0]
|
||||||
|
: searchParams?.autoAccept;
|
||||||
|
const callbackUrl = token
|
||||||
|
? `/teams?token=${encodeURIComponent(token)}${
|
||||||
|
autoAccept ? `&autoAccept=${encodeURIComponent(autoAccept)}` : ""
|
||||||
|
}`
|
||||||
|
: null;
|
||||||
|
|
||||||
if (!session) {
|
if (!session) {
|
||||||
redirect(callbackUrl ? `/auth/login?callbackUrl=${callbackUrl}` : "/auth/login");
|
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 { unstable_cache } from "next/cache";
|
||||||
|
|
||||||
import { TeamsListing } from "@calcom/features/ee/teams/components/TeamsListing";
|
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 { TeamRepository } from "@calcom/features/ee/teams/repositories/TeamRepository";
|
||||||
import { TeamService } from "@calcom/features/ee/teams/services/teamService";
|
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 prisma from "@calcom/prisma";
|
||||||
import { MembershipRole } from "@calcom/prisma/enums";
|
import { MembershipRole } from "@calcom/prisma/enums";
|
||||||
|
|
||||||
@@ -33,7 +33,9 @@ export const ServerTeamsListing = async ({
|
|||||||
session: Session;
|
session: Session;
|
||||||
}) => {
|
}) => {
|
||||||
const token = Array.isArray(searchParams?.token) ? searchParams.token[0] : searchParams?.token;
|
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;
|
const userId = session.user.id;
|
||||||
let invitationAccepted = false;
|
let invitationAccepted = false;
|
||||||
|
|
||||||
@@ -42,7 +44,7 @@ export const ServerTeamsListing = async ({
|
|||||||
|
|
||||||
if (token) {
|
if (token) {
|
||||||
try {
|
try {
|
||||||
if (autoAccept === "true") {
|
if (autoAccept === "true") {
|
||||||
await TeamService.acceptInvitationByToken(token, userId);
|
await TeamService.acceptInvitationByToken(token, userId);
|
||||||
invitationAccepted = true;
|
invitationAccepted = true;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -308,7 +308,7 @@ const WeightedAttributesSelector = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-muted mt-1 rounded-xl p-2">
|
<div className="bg-muted mt-1 rounded-xl p-2">
|
||||||
{!!attributeIdForWeights ? (
|
{attributeIdForWeights ? (
|
||||||
<SelectField
|
<SelectField
|
||||||
size="sm"
|
size="sm"
|
||||||
containerClassName="data-testid-select-router"
|
containerClassName="data-testid-select-router"
|
||||||
|
|||||||
@@ -76,7 +76,6 @@ export default function RoutingForms({ appUrl }: { appUrl: string }) {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
hookForm.reset({});
|
hookForm.reset({});
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, []);
|
}, []);
|
||||||
const filters = getTeamsFiltersFromQuery(routerQuery);
|
const filters = getTeamsFiltersFromQuery(routerQuery);
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import { cookies, headers } from "next/headers";
|
|||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
|
|
||||||
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
||||||
import { APP_NAME } from "@calcom/lib/constants";
|
|
||||||
import { UserRepository } from "@calcom/features/users/repositories/UserRepository";
|
import { UserRepository } from "@calcom/features/users/repositories/UserRepository";
|
||||||
|
import { APP_NAME } from "@calcom/lib/constants";
|
||||||
import prisma from "@calcom/prisma";
|
import prisma from "@calcom/prisma";
|
||||||
import { meRouter } from "@calcom/trpc/server/routers/viewer/me/_router";
|
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 LicenseRequired from "@calcom/features/ee/common/components/LicenseRequired";
|
||||||
import { OrgForm } from "@calcom/features/ee/organizations/pages/settings/admin/AdminOrgEditPage";
|
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 { OrganizationRepository } from "@calcom/features/ee/organizations/repositories/OrganizationRepository";
|
||||||
|
import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader";
|
||||||
|
|
||||||
const orgIdSchema = z.object({ id: z.coerce.number() });
|
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(
|
const tabMembers = Array.from(document.getElementsByTagName("a")).filter(
|
||||||
(bottom) => bottom.dataset.testid === "vertical-tab-Members"
|
(bottom) => bottom.dataset.testid === "vertical-tab-Members"
|
||||||
)[1];
|
)[1];
|
||||||
// eslint-disable-next-line @calcom/eslint/no-scroll-into-view-embed -- Settings layout isn't embedded
|
|
||||||
tabMembers?.scrollIntoView({ behavior: "smooth" });
|
tabMembers?.scrollIntoView({ behavior: "smooth" });
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
@@ -568,7 +567,6 @@ const SettingsSidebarContainer = ({
|
|||||||
const tabMembers = Array.from(document.getElementsByTagName("a")).filter(
|
const tabMembers = Array.from(document.getElementsByTagName("a")).filter(
|
||||||
(bottom) => bottom.dataset.testid === "vertical-tab-Members"
|
(bottom) => bottom.dataset.testid === "vertical-tab-Members"
|
||||||
)[1];
|
)[1];
|
||||||
// eslint-disable-next-line @calcom/eslint/no-scroll-into-view-embed -- Settings layout isn't embedded
|
|
||||||
tabMembers?.scrollIntoView({ behavior: "smooth" });
|
tabMembers?.scrollIntoView({ behavior: "smooth" });
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -2,9 +2,9 @@ import type { PageProps } from "app/_types";
|
|||||||
import { getTranslate, _generateMetadata } from "app/_utils";
|
import { getTranslate, _generateMetadata } from "app/_utils";
|
||||||
|
|
||||||
import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader";
|
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 { EditWebhookView } from "@calcom/features/webhooks/pages/webhook-edit-view";
|
||||||
import { APP_NAME } from "@calcom/lib/constants";
|
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 }> }) =>
|
export const generateMetadata = async ({ params }: { params: Promise<{ id: string }> }) =>
|
||||||
await _generateMetadata(
|
await _generateMetadata(
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
import { _generateMetadata, getTranslate } from "app/_utils";
|
import { _generateMetadata, getTranslate } from "app/_utils";
|
||||||
import { unstable_cache } from "next/cache";
|
import { unstable_cache } from "next/cache";
|
||||||
import { notFound } from "next/navigation";
|
|
||||||
import { revalidatePath } from "next/cache";
|
import { revalidatePath } from "next/cache";
|
||||||
|
import { notFound } from "next/navigation";
|
||||||
|
|
||||||
import type { AppFlags } from "@calcom/features/flags/config";
|
import type { AppFlags } from "@calcom/features/flags/config";
|
||||||
import { FeaturesRepository } from "@calcom/features/flags/features.repository";
|
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 { redirect } from "next/navigation";
|
||||||
|
|
||||||
import { OtherTeamsListing } from "@calcom/features/ee/organizations/pages/components/OtherTeamsListing";
|
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 { OrganizationRepository } from "@calcom/features/ee/organizations/repositories/OrganizationRepository";
|
||||||
|
import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader";
|
||||||
|
|
||||||
import { validateUserHasOrg } from "../../../actions/validateUserHasOrg";
|
import { validateUserHasOrg } from "../../../actions/validateUserHasOrg";
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ const resolveEndpoint = (links: any) => {
|
|||||||
// to the correct API endpoints.
|
// to the correct API endpoints.
|
||||||
// - viewer.me - 2 segment paths like this are for logged in requests
|
// - viewer.me - 2 segment paths like this are for logged in requests
|
||||||
// - viewer.public.i18n - 3 segments paths can be public or authed
|
// - 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
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
return (ctx: any) => {
|
return (ctx: any) => {
|
||||||
const parts = ctx.op.path.split(".");
|
const parts = ctx.op.path.split(".");
|
||||||
@@ -24,11 +23,7 @@ const resolveEndpoint = (links: any) => {
|
|||||||
if (parts.length == 2) {
|
if (parts.length == 2) {
|
||||||
endpoint = parts[0] as keyof typeof links;
|
endpoint = parts[0] as keyof typeof links;
|
||||||
path = parts[1];
|
path = parts[1];
|
||||||
} else if (parts.length >= 3 && parts[2] === "heavy") {
|
} else {
|
||||||
endpoint = parts[1] + "/heavy" as keyof typeof links;
|
|
||||||
path = parts[3];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
endpoint = parts[1] as keyof typeof links;
|
endpoint = parts[1] as keyof typeof links;
|
||||||
path = parts.splice(2, parts.length - 2).join(".");
|
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 { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
||||||
import { CalendarCache } from "@calcom/features/calendar-cache/calendar-cache";
|
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 { HttpError } from "@calcom/lib/http-error";
|
||||||
import notEmpty from "@calcom/lib/notEmpty";
|
import notEmpty from "@calcom/lib/notEmpty";
|
||||||
import { SelectedCalendarRepository } from "@calcom/lib/server/repository/selectedCalendar";
|
import { SelectedCalendarRepository } from "@calcom/lib/server/repository/selectedCalendar";
|
||||||
import { UserRepository } from "@calcom/features/users/repositories/UserRepository";
|
|
||||||
import prisma from "@calcom/prisma";
|
import prisma from "@calcom/prisma";
|
||||||
|
|
||||||
import { buildLegacyRequest } from "@lib/buildLegacyCtx";
|
import { buildLegacyRequest } from "@lib/buildLegacyCtx";
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ async function handler(req: NextRequest) {
|
|||||||
let appDirRequestBody;
|
let appDirRequestBody;
|
||||||
try {
|
try {
|
||||||
appDirRequestBody = await req.json();
|
appDirRequestBody = await req.json();
|
||||||
} catch (error) {
|
} catch {
|
||||||
return NextResponse.json({ success: false, message: "Invalid JSON" }, { status: 400 });
|
return NextResponse.json({ success: false, message: "Invalid JSON" }, { status: 400 });
|
||||||
}
|
}
|
||||||
const bookingData = bookingCancelWithCsrfSchema.parse(appDirRequestBody);
|
const bookingData = bookingCancelWithCsrfSchema.parse(appDirRequestBody);
|
||||||
@@ -26,11 +26,20 @@ async function handler(req: NextRequest) {
|
|||||||
cookieStore.delete("calcom.csrf_token");
|
cookieStore.delete("calcom.csrf_token");
|
||||||
|
|
||||||
const session = await getServerSession({ req: buildLegacyRequest(await headers(), await cookies()) });
|
const session = await getServerSession({ req: buildLegacyRequest(await headers(), await cookies()) });
|
||||||
|
|
||||||
const result = await handleCancelBooking({
|
const result = await handleCancelBooking({
|
||||||
bookingData,
|
bookingData,
|
||||||
userId: session?.user?.id || -1,
|
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;
|
const statusCode = result.success ? 200 : 400;
|
||||||
|
|
||||||
return NextResponse.json(result, { status: statusCode });
|
return NextResponse.json(result, { status: statusCode });
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import type { NextRequest } from "next/server";
|
import type { NextRequest } from "next/server";
|
||||||
import { NextResponse } 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 { DefaultAdapterFactory } from "@calcom/features/calendar-subscription/adapters/AdaptersFactory";
|
||||||
import { CalendarSubscriptionService } from "@calcom/features/calendar-subscription/lib/CalendarSubscriptionService";
|
import { CalendarSubscriptionService } from "@calcom/features/calendar-subscription/lib/CalendarSubscriptionService";
|
||||||
import { CalendarCacheEventRepository } from "@calcom/features/calendar-subscription/lib/cache/CalendarCacheEventRepository";
|
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 { CalendarSyncService } from "@calcom/features/calendar-subscription/lib/sync/CalendarSyncService";
|
||||||
import { FeaturesRepository } from "@calcom/features/flags/features.repository";
|
import { FeaturesRepository } from "@calcom/features/flags/features.repository";
|
||||||
import { SelectedCalendarRepository } from "@calcom/lib/server/repository/SelectedCalendarRepository";
|
import { SelectedCalendarRepository } from "@calcom/lib/server/repository/SelectedCalendarRepository";
|
||||||
import { BookingRepository } from "@calcom/features/bookings/repositories/BookingRepository";
|
|
||||||
import { prisma } from "@calcom/prisma";
|
import { prisma } from "@calcom/prisma";
|
||||||
import { defaultResponderForAppDir } from "@calcom/web/app/api/defaultResponderForAppDir";
|
import { defaultResponderForAppDir } from "@calcom/web/app/api/defaultResponderForAppDir";
|
||||||
|
|
||||||
|
|||||||
@@ -6,13 +6,13 @@
|
|||||||
import type { NextRequest } from "next/server";
|
import type { NextRequest } from "next/server";
|
||||||
import { NextResponse } 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 { 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 { HttpError } from "@calcom/lib/http-error";
|
||||||
import logger from "@calcom/lib/logger";
|
import logger from "@calcom/lib/logger";
|
||||||
import { safeStringify } from "@calcom/lib/safeStringify";
|
import { safeStringify } from "@calcom/lib/safeStringify";
|
||||||
import { CredentialRepository } from "@calcom/features/credentials/repositories/CredentialRepository";
|
|
||||||
import { SelectedCalendarRepository } from "@calcom/lib/server/repository/selectedCalendar";
|
import { SelectedCalendarRepository } from "@calcom/lib/server/repository/selectedCalendar";
|
||||||
import type { CredentialForCalendarServiceWithEmail } from "@calcom/types/Credential";
|
import type { CredentialForCalendarServiceWithEmail } from "@calcom/types/Credential";
|
||||||
import type { Ensure } from "@calcom/types/utils";
|
import type { Ensure } from "@calcom/types/utils";
|
||||||
|
|||||||
@@ -9,15 +9,15 @@ import {
|
|||||||
sendDailyVideoRecordingEmails,
|
sendDailyVideoRecordingEmails,
|
||||||
sendDailyVideoTranscriptEmails,
|
sendDailyVideoTranscriptEmails,
|
||||||
} from "@calcom/emails/daily-video-emails";
|
} from "@calcom/emails/daily-video-emails";
|
||||||
|
import {
|
||||||
|
getAllTranscriptsAccessLinkFromMeetingId,
|
||||||
|
submitBatchProcessorTranscriptionJob,
|
||||||
|
} from "@calcom/features/conferencing/lib/videoClient";
|
||||||
import { WEBAPP_URL } from "@calcom/lib/constants";
|
import { WEBAPP_URL } from "@calcom/lib/constants";
|
||||||
import { getTeamIdFromEventType } from "@calcom/lib/getTeamIdFromEventType";
|
import { getTeamIdFromEventType } from "@calcom/lib/getTeamIdFromEventType";
|
||||||
import { HttpError } from "@calcom/lib/http-error";
|
import { HttpError } from "@calcom/lib/http-error";
|
||||||
import logger from "@calcom/lib/logger";
|
import logger from "@calcom/lib/logger";
|
||||||
import { safeStringify } from "@calcom/lib/safeStringify";
|
import { safeStringify } from "@calcom/lib/safeStringify";
|
||||||
import {
|
|
||||||
getAllTranscriptsAccessLinkFromMeetingId,
|
|
||||||
submitBatchProcessorTranscriptionJob,
|
|
||||||
} from "@calcom/features/conferencing/lib/videoClient";
|
|
||||||
import { generateVideoToken } from "@calcom/lib/videoTokens";
|
import { generateVideoToken } from "@calcom/lib/videoTokens";
|
||||||
import prisma from "@calcom/prisma";
|
import prisma from "@calcom/prisma";
|
||||||
import { getBooking } from "@calcom/web/lib/daily-webhook/getBooking";
|
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 { BillingPlanService } from "@calcom/features/ee/billing/domain/billing-plans";
|
||||||
import type { Contact } from "@calcom/features/ee/support/lib/intercom/intercom";
|
import type { Contact } from "@calcom/features/ee/support/lib/intercom/intercom";
|
||||||
import { intercom } 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 { WEBAPP_URL, WEBSITE_URL } from "@calcom/lib/constants";
|
||||||
import logger from "@calcom/lib/logger";
|
import logger from "@calcom/lib/logger";
|
||||||
import { safeStringify } from "@calcom/lib/safeStringify";
|
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 { prisma } from "@calcom/prisma";
|
||||||
|
|
||||||
import { buildLegacyRequest } from "@lib/buildLegacyCtx";
|
import { buildLegacyRequest } from "@lib/buildLegacyCtx";
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import type { Params } from "app/_types";
|
|||||||
import type { NextRequest } from "next/server";
|
import type { NextRequest } from "next/server";
|
||||||
import { NextResponse } 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 type { CalendarSubscriptionProvider } from "@calcom/features/calendar-subscription/adapters/AdaptersFactory";
|
||||||
import { DefaultAdapterFactory } 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";
|
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 { FeaturesRepository } from "@calcom/features/flags/features.repository";
|
||||||
import logger from "@calcom/lib/logger";
|
import logger from "@calcom/lib/logger";
|
||||||
import { SelectedCalendarRepository } from "@calcom/lib/server/repository/SelectedCalendarRepository";
|
import { SelectedCalendarRepository } from "@calcom/lib/server/repository/SelectedCalendarRepository";
|
||||||
import { BookingRepository } from "@calcom/features/bookings/repositories/BookingRepository";
|
|
||||||
import { prisma } from "@calcom/prisma";
|
import { prisma } from "@calcom/prisma";
|
||||||
import { defaultResponderForAppDir } from "@calcom/web/app/api/defaultResponderForAppDir";
|
import { defaultResponderForAppDir } from "@calcom/web/app/api/defaultResponderForAppDir";
|
||||||
|
|
||||||
|
|||||||
@@ -696,7 +696,15 @@ describe("Retell AI Webhook Handler", () => {
|
|||||||
describe("Web Call Tests", () => {
|
describe("Web Call Tests", () => {
|
||||||
const mockAgent: Pick<
|
const mockAgent: Pick<
|
||||||
Agent,
|
Agent,
|
||||||
"id" | "name" | "providerAgentId" | "enabled" | "userId" | "teamId" | "createdAt" | "updatedAt" | "inboundEventTypeId"
|
| "id"
|
||||||
|
| "name"
|
||||||
|
| "providerAgentId"
|
||||||
|
| "enabled"
|
||||||
|
| "userId"
|
||||||
|
| "teamId"
|
||||||
|
| "createdAt"
|
||||||
|
| "updatedAt"
|
||||||
|
| "inboundEventTypeId"
|
||||||
> = {
|
> = {
|
||||||
id: "agent-123",
|
id: "agent-123",
|
||||||
name: "Test Agent",
|
name: "Test Agent",
|
||||||
|
|||||||
Vendored
+1
-1
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
import { revalidateTag, unstable_cache } from "next/cache";
|
import { revalidateTag, unstable_cache } from "next/cache";
|
||||||
|
|
||||||
import { NEXTJS_CACHE_TTL } from "@calcom/lib/constants";
|
|
||||||
import { MembershipRepository } from "@calcom/features/membership/repositories/MembershipRepository";
|
import { MembershipRepository } from "@calcom/features/membership/repositories/MembershipRepository";
|
||||||
|
import { NEXTJS_CACHE_TTL } from "@calcom/lib/constants";
|
||||||
|
|
||||||
const CACHE_TAGS = {
|
const CACHE_TAGS = {
|
||||||
HAS_TEAM_PLAN: "MembershipRepository.findFirstAcceptedMembershipByUserId",
|
HAS_TEAM_PLAN: "MembershipRepository.findFirstAcceptedMembershipByUserId",
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import z from "zod";
|
import z from "zod";
|
||||||
|
|
||||||
import type { PaymentPageProps } from "@calcom/features/ee/payments/pages/payment";
|
|
||||||
import { useBookingSuccessRedirect } from "@calcom/features/bookings/lib/bookingSuccessRedirect";
|
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 { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams";
|
||||||
import { useCopy } from "@calcom/lib/hooks/useCopy";
|
import { useCopy } from "@calcom/lib/hooks/useCopy";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
|
|||||||
@@ -115,7 +115,9 @@ export function getEditEventActions(context: BookingActionContext): ActionType[]
|
|||||||
icon: "clock",
|
icon: "clock",
|
||||||
label: t("reschedule_booking"),
|
label: t("reschedule_booking"),
|
||||||
href: `/reschedule/${booking.uid}${
|
href: `/reschedule/${booking.uid}${
|
||||||
booking.seatsReferences.length && isAttendee && seatReferenceUid ? `?seatReferenceUid=${seatReferenceUid}` : ""
|
booking.seatsReferences.length && isAttendee && seatReferenceUid
|
||||||
|
? `?seatReferenceUid=${seatReferenceUid}`
|
||||||
|
: ""
|
||||||
}`,
|
}`,
|
||||||
disabled:
|
disabled:
|
||||||
(isBookingInPast && !booking.eventType.allowReschedulingPastBookings) || isDisabledRescheduling,
|
(isBookingInPast && !booking.eventType.allowReschedulingPastBookings) || isDisabledRescheduling,
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ const useReroutingState = ({ isOpenDialog }: Pick<RerouteDialogProps, "isOpenDia
|
|||||||
const status = (() => {
|
const status = (() => {
|
||||||
if (!value) return ReroutingStatusEnum.REROUTING_NOT_INITIATED;
|
if (!value) return ReroutingStatusEnum.REROUTING_NOT_INITIATED;
|
||||||
if (value.error) return ReroutingStatusEnum.REROUTING_FAILED;
|
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;
|
return ReroutingStatusEnum.REROUTING_IN_PROGRESS;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ const UserProfile = ({ user }: UserProfileProps) => {
|
|||||||
const [imageSrc, setImageSrc] = useState<string>(user?.avatar || "");
|
const [imageSrc, setImageSrc] = useState<string>(user?.avatar || "");
|
||||||
const utils = trpc.useUtils();
|
const utils = trpc.useUtils();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const createEventType = trpc.viewer.eventTypes.heavy.create.useMutation();
|
const createEventType = trpc.viewer.eventTypesHeavy.create.useMutation();
|
||||||
const telemetry = useTelemetry();
|
const telemetry = useTelemetry();
|
||||||
const [firstRender, setFirstRender] = useState(true);
|
const [firstRender, setFirstRender] = useState(true);
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { ThemeProvider } from "next-themes";
|
|||||||
import type { AppProps as NextAppProps } from "next/app";
|
import type { AppProps as NextAppProps } from "next/app";
|
||||||
import type { ReadonlyURLSearchParams } from "next/navigation";
|
import type { ReadonlyURLSearchParams } from "next/navigation";
|
||||||
import { usePathname, useSearchParams } 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 DynamicPostHogProvider from "@calcom/features/ee/event-tracking/lib/posthog/providerDynamic";
|
||||||
import { OrgBrandingProvider } from "@calcom/features/ee/organizations/context/provider";
|
import { OrgBrandingProvider } from "@calcom/features/ee/organizations/context/provider";
|
||||||
@@ -119,15 +120,17 @@ const AppProviders = (props: PageWrapperProps) => {
|
|||||||
nonce={props.nonce}
|
nonce={props.nonce}
|
||||||
isThemeSupported={isThemeSupported}
|
isThemeSupported={isThemeSupported}
|
||||||
isBookingPage={props.isBookingPage || isBookingPage}>
|
isBookingPage={props.isBookingPage || isBookingPage}>
|
||||||
<FeatureFlagsProvider>
|
<NuqsAdapter>
|
||||||
{props.isBookingPage || isBookingPage ? (
|
<FeatureFlagsProvider>
|
||||||
<OrgBrandProvider>{props.children}</OrgBrandProvider>
|
{props.isBookingPage || isBookingPage ? (
|
||||||
) : (
|
|
||||||
<DynamicIntercomProvider>
|
|
||||||
<OrgBrandProvider>{props.children}</OrgBrandProvider>
|
<OrgBrandProvider>{props.children}</OrgBrandProvider>
|
||||||
</DynamicIntercomProvider>
|
) : (
|
||||||
)}
|
<DynamicIntercomProvider>
|
||||||
</FeatureFlagsProvider>
|
<OrgBrandProvider>{props.children}</OrgBrandProvider>
|
||||||
|
</DynamicIntercomProvider>
|
||||||
|
)}
|
||||||
|
</FeatureFlagsProvider>
|
||||||
|
</NuqsAdapter>
|
||||||
</CalcomThemeProvider>
|
</CalcomThemeProvider>
|
||||||
</TooltipProvider>
|
</TooltipProvider>
|
||||||
</EventCollectionProvider>
|
</EventCollectionProvider>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import type { SSRConfig } from "next-i18next/dist/types/types";
|
|||||||
import { ThemeProvider } from "next-themes";
|
import { ThemeProvider } from "next-themes";
|
||||||
import type { AppProps as NextAppProps, AppProps as NextJsAppProps } from "next/app";
|
import type { AppProps as NextAppProps, AppProps as NextJsAppProps } from "next/app";
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
|
import { NuqsAdapter } from "nuqs/adapters/next/pages";
|
||||||
import type { ParsedUrlQuery } from "querystring";
|
import type { ParsedUrlQuery } from "querystring";
|
||||||
import type { PropsWithChildren, ReactNode } from "react";
|
import type { PropsWithChildren, ReactNode } from "react";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
@@ -286,15 +287,17 @@ const AppProviders = (props: AppPropsWithChildren) => {
|
|||||||
isThemeSupported={props.Component.isThemeSupported}
|
isThemeSupported={props.Component.isThemeSupported}
|
||||||
isBookingPage={props.Component.isBookingPage || isBookingPage}
|
isBookingPage={props.Component.isBookingPage || isBookingPage}
|
||||||
router={props.router}>
|
router={props.router}>
|
||||||
<FeatureFlagsProvider>
|
<NuqsAdapter>
|
||||||
{_isBookingPage ? (
|
<FeatureFlagsProvider>
|
||||||
<OrgBrandProvider>{props.children}</OrgBrandProvider>
|
{_isBookingPage ? (
|
||||||
) : (
|
|
||||||
<DynamicIntercomProvider>
|
|
||||||
<OrgBrandProvider>{props.children}</OrgBrandProvider>
|
<OrgBrandProvider>{props.children}</OrgBrandProvider>
|
||||||
</DynamicIntercomProvider>
|
) : (
|
||||||
)}
|
<DynamicIntercomProvider>
|
||||||
</FeatureFlagsProvider>
|
<OrgBrandProvider>{props.children}</OrgBrandProvider>
|
||||||
|
</DynamicIntercomProvider>
|
||||||
|
)}
|
||||||
|
</FeatureFlagsProvider>
|
||||||
|
</NuqsAdapter>
|
||||||
</CalcomThemeProvider>
|
</CalcomThemeProvider>
|
||||||
</TooltipProvider>
|
</TooltipProvider>
|
||||||
</CustomI18nextProvider>
|
</CustomI18nextProvider>
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ import type { LocationObject } from "@calcom/app-store/locations";
|
|||||||
import { isConferencing as isConferencingApp } from "@calcom/app-store/utils";
|
import { isConferencing as isConferencingApp } from "@calcom/app-store/utils";
|
||||||
import { getLocale } from "@calcom/features/auth/lib/getLocale";
|
import { getLocale } from "@calcom/features/auth/lib/getLocale";
|
||||||
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
||||||
|
import { UserRepository } from "@calcom/features/users/repositories/UserRepository";
|
||||||
import { AppOnboardingSteps } from "@calcom/lib/apps/appOnboardingSteps";
|
import { AppOnboardingSteps } from "@calcom/lib/apps/appOnboardingSteps";
|
||||||
import { CAL_URL } from "@calcom/lib/constants";
|
import { CAL_URL } from "@calcom/lib/constants";
|
||||||
import { getPlaceholderAvatar } from "@calcom/lib/defaultAvatarImage";
|
import { getPlaceholderAvatar } from "@calcom/lib/defaultAvatarImage";
|
||||||
import { UserRepository } from "@calcom/features/users/repositories/UserRepository";
|
|
||||||
import prisma from "@calcom/prisma";
|
import prisma from "@calcom/prisma";
|
||||||
import type { Prisma } from "@calcom/prisma/client";
|
import type { Prisma } from "@calcom/prisma/client";
|
||||||
import { eventTypeBookingFields } from "@calcom/prisma/zod-utils";
|
import { eventTypeBookingFields } from "@calcom/prisma/zod-utils";
|
||||||
@@ -281,7 +281,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
|
|||||||
id: user.id,
|
id: user.id,
|
||||||
name: user.name,
|
name: user.name,
|
||||||
avatarUrl: user.avatarUrl,
|
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
|
const teamsWithIsAppInstalled = hasTeams
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { enrichFormWithMigrationData } from "@calcom/app-store/routing-forms/enrichFormWithMigrationData";
|
import { enrichFormWithMigrationData } from "@calcom/app-store/routing-forms/enrichFormWithMigrationData";
|
||||||
import { getSerializableForm } from "@calcom/app-store/routing-forms/lib/getSerializableForm";
|
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 { Resource } from "@calcom/features/pbac/domain/types/permission-registry";
|
||||||
import { getResourcePermissions } from "@calcom/features/pbac/lib/resource-permissions";
|
import { getResourcePermissions } from "@calcom/features/pbac/lib/resource-permissions";
|
||||||
import { MembershipRepository } from "@calcom/features/membership/repositories/MembershipRepository";
|
|
||||||
import { MembershipRole } from "@calcom/prisma/enums";
|
import { MembershipRole } from "@calcom/prisma/enums";
|
||||||
import type { AppGetServerSidePropsContext, AppPrisma, AppUser } from "@calcom/types/AppGetServerSideProps";
|
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 { orgDomainConfig } from "@calcom/features/ee/organizations/lib/orgDomains";
|
||||||
import { EventRepository } from "@calcom/features/eventtypes/repositories/EventRepository";
|
import { EventRepository } from "@calcom/features/eventtypes/repositories/EventRepository";
|
||||||
import { FeaturesRepository } from "@calcom/features/flags/features.repository";
|
import { FeaturesRepository } from "@calcom/features/flags/features.repository";
|
||||||
import { HashedLinkService } from "@calcom/features/hashedLink/services/hashedLinkService";
|
|
||||||
import {
|
import {
|
||||||
shouldHideBrandingForTeamEvent,
|
shouldHideBrandingForTeamEvent,
|
||||||
shouldHideBrandingForUserEvent,
|
shouldHideBrandingForUserEvent,
|
||||||
} from "@calcom/features/profile/lib/hideBranding";
|
} from "@calcom/features/profile/lib/hideBranding";
|
||||||
import { UserRepository } from "@calcom/features/users/repositories/UserRepository";
|
import { UserRepository } from "@calcom/features/users/repositories/UserRepository";
|
||||||
|
import { HashedLinkService } from "@calcom/features/hashedLink/lib/service/HashedLinkService";
|
||||||
import slugify from "@calcom/lib/slugify";
|
import slugify from "@calcom/lib/slugify";
|
||||||
import prisma from "@calcom/prisma";
|
import prisma from "@calcom/prisma";
|
||||||
import { RedirectType } from "@calcom/prisma/enums";
|
import { RedirectType } from "@calcom/prisma/enums";
|
||||||
@@ -82,9 +82,8 @@ async function getUserPageProps(context: GetServerSidePropsContext) {
|
|||||||
redirect: {
|
redirect: {
|
||||||
permanent: false,
|
permanent: false,
|
||||||
// App Router doesn't have access to the current path directly, so we build it manually
|
// App Router doesn't have access to the current path directly, so we build it manually
|
||||||
destination: `${redirectWithOriginAndSearchString.origin ?? ""}/d/${link}/${slug}${
|
destination: `${redirectWithOriginAndSearchString.origin ?? ""}/d/${link}/${slug}${redirectWithOriginAndSearchString.searchString
|
||||||
redirectWithOriginAndSearchString.searchString
|
}`,
|
||||||
}`,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
|||||||
import { getMultipleDurationValue } from "@calcom/features/bookings/lib/get-booking";
|
import { getMultipleDurationValue } from "@calcom/features/bookings/lib/get-booking";
|
||||||
import { getSlugOrRequestedSlug } from "@calcom/features/ee/organizations/lib/orgDomains";
|
import { getSlugOrRequestedSlug } from "@calcom/features/ee/organizations/lib/orgDomains";
|
||||||
import { orgDomainConfig } 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 { EventRepository } from "@calcom/features/eventtypes/repositories/EventRepository";
|
||||||
|
import { shouldHideBrandingForTeamEvent } from "@calcom/features/profile/lib/hideBranding";
|
||||||
import slugify from "@calcom/lib/slugify";
|
import slugify from "@calcom/lib/slugify";
|
||||||
import prisma from "@calcom/prisma";
|
import prisma from "@calcom/prisma";
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ import type { NextApiRequest, NextApiResponse } from "next";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import dayjs from "@calcom/dayjs";
|
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 { StripeBillingService } from "@calcom/features/ee/billing/stripe-billing-service";
|
||||||
import { WEBAPP_URL } from "@calcom/lib/constants";
|
import { WEBAPP_URL } from "@calcom/lib/constants";
|
||||||
import { IS_STRIPE_ENABLED } 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 { prisma } from "@calcom/prisma";
|
||||||
import { MembershipRole } from "@calcom/prisma/enums";
|
import { MembershipRole } from "@calcom/prisma/enums";
|
||||||
import { CreationSource } from "@calcom/prisma/enums";
|
import { CreationSource } from "@calcom/prisma/enums";
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ export const getServerSideProps = async (ctx: GetServerSidePropsContext) => {
|
|||||||
"email-verification"
|
"email-verification"
|
||||||
);
|
);
|
||||||
const signupDisabled = await featuresRepository.checkIfFeatureIsEnabledGlobally("disable-signup");
|
const signupDisabled = await featuresRepository.checkIfFeatureIsEnabledGlobally("disable-signup");
|
||||||
|
const onboardingV3Enabled = await featuresRepository.checkIfFeatureIsEnabledGlobally("onboarding-v3");
|
||||||
|
|
||||||
const token = z.string().optional().parse(ctx.query.token);
|
const token = z.string().optional().parse(ctx.query.token);
|
||||||
const redirectUrlData = z
|
const redirectUrlData = z
|
||||||
@@ -62,6 +63,7 @@ export const getServerSideProps = async (ctx: GetServerSidePropsContext) => {
|
|||||||
isSAMLLoginEnabled,
|
isSAMLLoginEnabled,
|
||||||
prepopulateFormValues: undefined,
|
prepopulateFormValues: undefined,
|
||||||
emailVerificationEnabled,
|
emailVerificationEnabled,
|
||||||
|
onboardingV3Enabled,
|
||||||
};
|
};
|
||||||
|
|
||||||
if ((process.env.NEXT_PUBLIC_DISABLE_SIGNUP === "true" && !token) || signupDisabled) {
|
if ((process.env.NEXT_PUBLIC_DISABLE_SIGNUP === "true" && !token) || signupDisabled) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import type { GetServerSidePropsContext } from "next";
|
import type { GetServerSidePropsContext } from "next";
|
||||||
|
|
||||||
|
import { getBookerBaseUrlSync } from "@calcom/features/ee/organizations/lib/getBookerBaseUrlSync";
|
||||||
import { orgDomainConfig } from "@calcom/features/ee/organizations/lib/orgDomains";
|
import { orgDomainConfig } from "@calcom/features/ee/organizations/lib/orgDomains";
|
||||||
import {
|
import {
|
||||||
getOrganizationSettings,
|
getOrganizationSettings,
|
||||||
@@ -9,7 +10,6 @@ import { getTeamWithMembers } from "@calcom/features/ee/teams/lib/queries";
|
|||||||
import { FeaturesRepository } from "@calcom/features/flags/features.repository";
|
import { FeaturesRepository } from "@calcom/features/flags/features.repository";
|
||||||
import { IS_CALCOM } from "@calcom/lib/constants";
|
import { IS_CALCOM } from "@calcom/lib/constants";
|
||||||
import { getUserAvatarUrl } from "@calcom/lib/getAvatarUrl";
|
import { getUserAvatarUrl } from "@calcom/lib/getAvatarUrl";
|
||||||
import { getBookerBaseUrlSync } from "@calcom/features/ee/organizations/lib/getBookerBaseUrlSync";
|
|
||||||
import logger from "@calcom/lib/logger";
|
import logger from "@calcom/lib/logger";
|
||||||
import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML";
|
import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML";
|
||||||
import slugify from "@calcom/lib/slugify";
|
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,
|
updateMeetingTokenIfExpired,
|
||||||
} from "@calcom/app-store/dailyvideo/lib/VideoApiAdapter";
|
} from "@calcom/app-store/dailyvideo/lib/VideoApiAdapter";
|
||||||
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
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 { EventTypeRepository } from "@calcom/features/eventtypes/repositories/eventTypeRepository";
|
||||||
import { FeaturesRepository } from "@calcom/features/flags/features.repository";
|
import { FeaturesRepository } from "@calcom/features/flags/features.repository";
|
||||||
import { getCalVideoReference } from "@calcom/features/get-cal-video-reference";
|
import { getCalVideoReference } from "@calcom/features/get-cal-video-reference";
|
||||||
import { UserRepository } from "@calcom/features/users/repositories/UserRepository";
|
import { UserRepository } from "@calcom/features/users/repositories/UserRepository";
|
||||||
import { CAL_VIDEO_MEETING_LINK_FOR_TESTING } from "@calcom/lib/constants";
|
import { CAL_VIDEO_MEETING_LINK_FOR_TESTING } from "@calcom/lib/constants";
|
||||||
import { isENVDev } from "@calcom/lib/env";
|
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";
|
import prisma from "@calcom/prisma";
|
||||||
|
|
||||||
const md = new MarkdownIt("default", { html: true, breaks: true, linkify: true });
|
const md = new MarkdownIt("default", { html: true, breaks: true, linkify: true });
|
||||||
@@ -210,7 +210,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
|
|||||||
exp: epochTimeFourteenDaysAfter,
|
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({
|
const isOrganizer = await checkIfUserIsHost({
|
||||||
booking: {
|
booking: {
|
||||||
eventTypeId: bookingObj.eventType?.id,
|
eventTypeId: bookingObj.eventType?.id,
|
||||||
@@ -238,7 +238,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
|
|||||||
videoReferencePassword,
|
videoReferencePassword,
|
||||||
sessionUserId
|
sessionUserId
|
||||||
);
|
);
|
||||||
if (!!meetingPassword) {
|
if (meetingPassword) {
|
||||||
bookingObj.references.forEach((bookRef) => {
|
bookingObj.references.forEach((bookRef) => {
|
||||||
bookRef.meetingPassword = meetingPassword;
|
bookRef.meetingPassword = meetingPassword;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import type { z } from "zod";
|
|||||||
|
|
||||||
import checkForMultiplePaymentApps from "@calcom/app-store/_utils/payments/checkForMultiplePaymentApps";
|
import checkForMultiplePaymentApps from "@calcom/app-store/_utils/payments/checkForMultiplePaymentApps";
|
||||||
import useAddAppMutation from "@calcom/app-store/_utils/useAddAppMutation";
|
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 type { EventTypeAppSettingsComponentProps, EventTypeModel } from "@calcom/app-store/types";
|
||||||
import { eventTypeMetaDataSchemaWithTypedApps } from "@calcom/app-store/zod-utils";
|
import { eventTypeMetaDataSchemaWithTypedApps } from "@calcom/app-store/zod-utils";
|
||||||
import type { LocationFormValues } from "@calcom/features/eventtypes/lib/types";
|
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 { getAppOnboardingUrl } from "@calcom/lib/apps/getAppOnboardingUrl";
|
||||||
import { WEBAPP_URL } from "@calcom/lib/constants";
|
import { WEBAPP_URL } from "@calcom/lib/constants";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import type { LocationObject } from "@calcom/app-store/locations";
|
|
||||||
import type { Team } from "@calcom/prisma/client";
|
import type { Team } from "@calcom/prisma/client";
|
||||||
import type { eventTypeBookingFields } from "@calcom/prisma/zod-utils";
|
import type { eventTypeBookingFields } from "@calcom/prisma/zod-utils";
|
||||||
import type { EventTypeMetaDataSchema } 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
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [eventTypeGroups]);
|
}, [eventTypeGroups]);
|
||||||
|
|
||||||
const updateMutation = trpc.viewer.eventTypes.heavy.update.useMutation({
|
const updateMutation = trpc.viewer.eventTypesHeavy.update.useMutation({
|
||||||
onSuccess: async (data) => {
|
onSuccess: async (data) => {
|
||||||
showToast(t("event_type_updated_successfully", { eventTypeTitle: data.eventType?.title }), "success");
|
showToast(t("event_type_updated_successfully", { eventTypeTitle: data.eventType?.title }), "success");
|
||||||
},
|
},
|
||||||
@@ -309,21 +309,20 @@ const OnboardingPage = ({
|
|||||||
handleSetUpLater={handleSetUpLater}
|
handleSetUpLater={handleSetUpLater}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{currentStep === AppOnboardingSteps.CONFIGURE_STEP &&
|
{currentStep === AppOnboardingSteps.CONFIGURE_STEP && eventTypeGroups && (
|
||||||
eventTypeGroups && (
|
<ConfigureStepCard
|
||||||
<ConfigureStepCard
|
slug={appMetadata.slug}
|
||||||
slug={appMetadata.slug}
|
categories={appMetadata.categories}
|
||||||
categories={appMetadata.categories}
|
credentialId={credentialId}
|
||||||
credentialId={credentialId}
|
userName={userName}
|
||||||
userName={userName}
|
loading={updateMutation.isPending}
|
||||||
loading={updateMutation.isPending}
|
formPortalRef={formPortalRef}
|
||||||
formPortalRef={formPortalRef}
|
setConfigureStep={setConfigureStep}
|
||||||
setConfigureStep={setConfigureStep}
|
eventTypeGroups={eventTypeGroups}
|
||||||
eventTypeGroups={eventTypeGroups}
|
handleSetUpLater={handleSetUpLater}
|
||||||
handleSetUpLater={handleSetUpLater}
|
isConferencing={isConferencing}
|
||||||
isConferencing={isConferencing}
|
/>
|
||||||
/>
|
)}
|
||||||
)}
|
|
||||||
</Form>
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
</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