Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aedc0f5a03 | ||
|
|
ff88ec0eae | ||
|
|
110b5ceaf6 | ||
|
|
7c53d2c9ce | ||
|
|
eccebff4c6 | ||
|
|
0a36dd7f61 | ||
|
|
5143083efb | ||
|
|
b874e0d55a | ||
|
|
3ee24586a7 | ||
|
|
b014ded5fd |
+1
-1
@@ -10,7 +10,7 @@ ARG CALCOM_TELEMETRY_DISABLED
|
||||
ARG DATABASE_URL
|
||||
ARG NEXTAUTH_SECRET=secret
|
||||
ARG CALENDSO_ENCRYPTION_KEY=secret
|
||||
ARG MAX_OLD_SPACE_SIZE=4096
|
||||
ARG MAX_OLD_SPACE_SIZE=6144
|
||||
ARG NEXT_PUBLIC_API_V2_URL
|
||||
ARG CSP_POLICY
|
||||
|
||||
|
||||
+162
-2
@@ -34,6 +34,7 @@ import { SchedulingType } from "@calcom/platform-libraries";
|
||||
import {
|
||||
BaseConfirmationPolicy_2024_06_14,
|
||||
TeamEventTypeOutput_2024_06_14,
|
||||
supportedIntegrations,
|
||||
type ApiSuccessResponse,
|
||||
type CreateEventTypeInput_2024_06_14,
|
||||
type EventTypeOutput_2024_06_14,
|
||||
@@ -640,9 +641,9 @@ describe("Event types Endpoints", () => {
|
||||
hiddenEventType = responseBody.data;
|
||||
});
|
||||
|
||||
it(`/GET/event-types by username`, async () => {
|
||||
it(`/GET/event-types by username with sortCreatedAt=desc`, async () => {
|
||||
const response = await request(app.getHttpServer())
|
||||
.get(`/api/v2/event-types?username=${user.username}`)
|
||||
.get(`/api/v2/event-types?username=${user.username}&sortCreatedAt=desc`)
|
||||
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
||||
.set("Authorization", `Bearer ${apiKeyString}`)
|
||||
.expect(200);
|
||||
@@ -653,6 +654,12 @@ describe("Event types Endpoints", () => {
|
||||
expect(responseBody.data).toBeDefined();
|
||||
expect(responseBody.data?.length).toEqual(2);
|
||||
|
||||
// Verify ordering: event types are returned newest to oldest when sortCreatedAt=desc
|
||||
// hiddenEventType was created after eventType, so it should have a higher ID and appear first
|
||||
expect(responseBody.data[0].id).toBeGreaterThan(responseBody.data[1].id);
|
||||
expect(responseBody.data[0].id).toEqual(hiddenEventType.id);
|
||||
expect(responseBody.data[1].id).toEqual(eventType.id);
|
||||
|
||||
const fetchedEventType = responseBody.data?.find((et) => et.id === eventType.id);
|
||||
const fetchedHiddenEventType = responseBody.data?.find((et) => et.id === hiddenEventType.id);
|
||||
|
||||
@@ -711,6 +718,46 @@ describe("Event types Endpoints", () => {
|
||||
expect(fetchedEventType?.hidden).toEqual(false);
|
||||
});
|
||||
|
||||
it(`/GET/event-types by username with sortCreatedAt=asc`, async () => {
|
||||
const response = await request(app.getHttpServer())
|
||||
.get(`/api/v2/event-types?username=${user.username}&sortCreatedAt=asc`)
|
||||
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
||||
.set("Authorization", `Bearer ${apiKeyString}`)
|
||||
.expect(200);
|
||||
|
||||
const responseBody: ApiSuccessResponse<EventTypeOutput_2024_06_14[]> = response.body;
|
||||
|
||||
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||
expect(responseBody.data).toBeDefined();
|
||||
expect(responseBody.data?.length).toEqual(2);
|
||||
|
||||
// Verify ordering: event types are returned oldest to newest when sortCreatedAt=asc
|
||||
// eventType was created before hiddenEventType, so it should have a lower ID and appear first
|
||||
expect(responseBody.data[0].id).toBeLessThan(responseBody.data[1].id);
|
||||
expect(responseBody.data[0].id).toEqual(eventType.id);
|
||||
expect(responseBody.data[1].id).toEqual(hiddenEventType.id);
|
||||
});
|
||||
|
||||
it(`/GET/event-types by username without sortCreatedAt parameter`, async () => {
|
||||
const response = await request(app.getHttpServer())
|
||||
.get(`/api/v2/event-types?username=${user.username}`)
|
||||
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
||||
.set("Authorization", `Bearer ${apiKeyString}`)
|
||||
.expect(200);
|
||||
|
||||
const responseBody: ApiSuccessResponse<EventTypeOutput_2024_06_14[]> = response.body;
|
||||
|
||||
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||
expect(responseBody.data).toBeDefined();
|
||||
expect(responseBody.data?.length).toEqual(2);
|
||||
|
||||
// Without sortCreatedAt, no specific order is guaranteed
|
||||
// Just verify both event types are present
|
||||
const ids = responseBody.data.map((et) => et.id);
|
||||
expect(ids).toContain(eventType.id);
|
||||
expect(ids).toContain(hiddenEventType.id);
|
||||
});
|
||||
|
||||
it(`/GET/event-types by username should not return hidden event type if no auth provided`, async () => {
|
||||
const response = await request(app.getHttpServer())
|
||||
.get(`/api/v2/event-types?username=${user.username}`)
|
||||
@@ -2855,6 +2902,119 @@ describe("Event types Endpoints", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("should accept newly supported integration locations in input validation", async () => {
|
||||
// Test that the API accepts various newly supported integrations by creating event types
|
||||
// with these location types directly in the database and then retrieving them
|
||||
// Note: Database uses underscores in integration types, API returns hyphens
|
||||
const integrationTests = [
|
||||
{ internal: "integrations:jitsi", api: "jitsi" },
|
||||
{ internal: "integrations:whereby_video", api: "whereby-video" },
|
||||
{ internal: "integrations:huddle01", api: "huddle" },
|
||||
{ internal: "integrations:tandem", api: "tandem" },
|
||||
{ internal: "integrations:element-call_video", api: "element-call-video" },
|
||||
];
|
||||
|
||||
for (const { internal, api } of integrationTests) {
|
||||
const eventTypeInput = {
|
||||
title: `event type ${api}`,
|
||||
description: "event type description",
|
||||
length: 30,
|
||||
hidden: false,
|
||||
slug: `event-type-${api}`,
|
||||
locations: [
|
||||
{
|
||||
type: internal,
|
||||
link: `https://example.com/${api}/meeting`,
|
||||
},
|
||||
],
|
||||
schedulingType: SchedulingType.ROUND_ROBIN,
|
||||
bookingFields: [],
|
||||
};
|
||||
|
||||
const legacyEventType = await eventTypesRepositoryFixture.create(eventTypeInput, user.id);
|
||||
|
||||
const response = await request(app.getHttpServer())
|
||||
.get(`/api/v2/event-types/${legacyEventType.id}`)
|
||||
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
||||
.expect(200);
|
||||
|
||||
const responseBody: ApiSuccessResponse<EventTypeOutput_2024_06_14> = response.body;
|
||||
const fetchedEventType = responseBody.data;
|
||||
|
||||
// Verify the integration location is returned correctly
|
||||
expect(fetchedEventType.locations).toHaveLength(1);
|
||||
expect(fetchedEventType.locations[0]).toMatchObject({
|
||||
type: "integration",
|
||||
integration: api,
|
||||
});
|
||||
|
||||
// Clean up
|
||||
await eventTypesRepositoryFixture.delete(legacyEventType.id);
|
||||
}
|
||||
});
|
||||
|
||||
it("should reject with 400 if creating event type with unsupported integration location", async () => {
|
||||
const createPayload = {
|
||||
title: "Event with unsupported integration",
|
||||
slug: "event-with-unsupported-integration",
|
||||
lengthInMinutes: 30,
|
||||
locations: [
|
||||
{
|
||||
type: "integration",
|
||||
integration: "unsupported-integration",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const response = await request(app.getHttpServer())
|
||||
.post("/api/v2/event-types")
|
||||
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
||||
.send(createPayload);
|
||||
|
||||
expect(response.status).toBe(400);
|
||||
expect(response.body.error.message).toBe(
|
||||
`Validation failed for integration location: integration must be one of the following values: ${supportedIntegrations.join(
|
||||
", "
|
||||
)}`
|
||||
);
|
||||
});
|
||||
|
||||
it("should reject with 400 if patching event type with integration that user has not connected", async () => {
|
||||
const createPayload = {
|
||||
title: "Event for patch test",
|
||||
slug: "event-patch-test-unconnected-integration",
|
||||
lengthInMinutes: 30,
|
||||
};
|
||||
|
||||
const createResponse = await request(app.getHttpServer())
|
||||
.post("/api/v2/event-types")
|
||||
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
||||
.send(createPayload)
|
||||
.expect(201);
|
||||
|
||||
const createdEventType = createResponse.body.data;
|
||||
|
||||
const patchPayload = {
|
||||
locations: [
|
||||
{
|
||||
type: "integration",
|
||||
integration: "jitsi",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const patchResponse = await request(app.getHttpServer())
|
||||
.patch(`/api/v2/event-types/${createdEventType.id}`)
|
||||
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
||||
.send(patchPayload);
|
||||
|
||||
expect(patchResponse.status).toBe(400);
|
||||
expect(patchResponse.body.error.message).toBe("jitsi not connected.");
|
||||
|
||||
// Cleanup
|
||||
await eventTypesRepositoryFixture.delete(createdEventType.id);
|
||||
});
|
||||
|
||||
describe("EventType Hidden Property", () => {
|
||||
let createdEventTypeId: number;
|
||||
|
||||
|
||||
+2
@@ -155,6 +155,8 @@ export class EventTypesController_2024_06_14 {
|
||||
summary: "Get all event types",
|
||||
description: `Hidden event types are returned only if authentication is provided and it belongs to the event type owner.
|
||||
|
||||
Use the optional \`sortCreatedAt\` query parameter to order results by creation date (by ID). Accepts "asc" (oldest first) or "desc" (newest first). When not provided, no explicit ordering is applied.
|
||||
|
||||
<Note>Please make sure to pass in the cal-api-version header value as mentioned in the Headers section. Not passing the correct value will default to an older version of this endpoint.</Note>
|
||||
`,
|
||||
})
|
||||
|
||||
@@ -3,6 +3,7 @@ import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
|
||||
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
|
||||
import { Injectable } from "@nestjs/common";
|
||||
|
||||
import type { SortOrderType } from "@calcom/platform-types";
|
||||
import type { Prisma } from "@calcom/prisma/client";
|
||||
|
||||
@Injectable()
|
||||
@@ -67,21 +68,23 @@ export class EventTypesRepository_2024_06_14 {
|
||||
});
|
||||
}
|
||||
|
||||
async getUserEventTypes(userId: number) {
|
||||
async getUserEventTypes(userId: number, sortCreatedAt?: SortOrderType) {
|
||||
return this.dbRead.prisma.eventType.findMany({
|
||||
where: {
|
||||
userId,
|
||||
},
|
||||
...(sortCreatedAt && { orderBy: { id: sortCreatedAt } }),
|
||||
include: { users: true, schedule: true, destinationCalendar: true },
|
||||
});
|
||||
}
|
||||
|
||||
async getUserEventTypesPublic(userId: number) {
|
||||
async getUserEventTypesPublic(userId: number, sortCreatedAt?: SortOrderType) {
|
||||
return this.dbRead.prisma.eventType.findMany({
|
||||
where: {
|
||||
userId,
|
||||
hidden: false,
|
||||
},
|
||||
...(sortCreatedAt && { orderBy: { id: sortCreatedAt } }),
|
||||
include: { users: true, schedule: true, destinationCalendar: true },
|
||||
});
|
||||
}
|
||||
|
||||
+11
-9
@@ -22,7 +22,7 @@ import {
|
||||
getEventTypesPublic,
|
||||
EventTypesPublic,
|
||||
} from "@calcom/platform-libraries/event-types";
|
||||
import type { GetEventTypesQuery_2024_06_14 } from "@calcom/platform-types";
|
||||
import type { GetEventTypesQuery_2024_06_14, SortOrderType } from "@calcom/platform-types";
|
||||
import type { EventType } from "@calcom/prisma/client";
|
||||
|
||||
@Injectable()
|
||||
@@ -160,15 +160,16 @@ export class EventTypesService_2024_06_14 {
|
||||
orgSlug?: string;
|
||||
orgId?: number;
|
||||
authUser?: AuthOptionalUser;
|
||||
sortCreatedAt?: SortOrderType;
|
||||
}) {
|
||||
const user = await this.usersRepository.findByUsername(params.username, params.orgSlug, params.orgId);
|
||||
if (!user) {
|
||||
return [];
|
||||
}
|
||||
if (params.authUser?.id !== user.id) {
|
||||
return await this.getUserEventTypesPublic(user.id);
|
||||
return await this.getUserEventTypesPublic(user.id, params.sortCreatedAt);
|
||||
}
|
||||
return await this.getUserEventTypes(user.id);
|
||||
return await this.getUserEventTypes(user.id, params.sortCreatedAt);
|
||||
}
|
||||
|
||||
async getUserToCreateEvent(user: UserWithProfile) {
|
||||
@@ -211,16 +212,16 @@ export class EventTypesService_2024_06_14 {
|
||||
};
|
||||
}
|
||||
|
||||
async getUserEventTypes(userId: number) {
|
||||
const eventTypes = await this.eventTypesRepository.getUserEventTypes(userId);
|
||||
async getUserEventTypes(userId: number, sortCreatedAt?: SortOrderType) {
|
||||
const eventTypes = await this.eventTypesRepository.getUserEventTypes(userId, sortCreatedAt);
|
||||
|
||||
return eventTypes.map((eventType) => {
|
||||
return { ownerId: userId, ...eventType };
|
||||
});
|
||||
}
|
||||
|
||||
async getUserEventTypesPublic(userId: number) {
|
||||
const eventTypes = await this.eventTypesRepository.getUserEventTypesPublic(userId);
|
||||
async getUserEventTypesPublic(userId: number, sortCreatedAt?: SortOrderType) {
|
||||
const eventTypes = await this.eventTypesRepository.getUserEventTypesPublic(userId, sortCreatedAt);
|
||||
|
||||
return eventTypes.map((eventType) => {
|
||||
return { ownerId: userId, ...eventType };
|
||||
@@ -237,7 +238,7 @@ export class EventTypesService_2024_06_14 {
|
||||
}
|
||||
|
||||
async getEventTypes(queryParams: GetEventTypesQuery_2024_06_14, authUser?: AuthOptionalUser) {
|
||||
const { username, eventSlug, usernames, orgSlug, orgId } = queryParams;
|
||||
const { username, eventSlug, usernames, orgSlug, orgId, sortCreatedAt } = queryParams;
|
||||
if (username && eventSlug) {
|
||||
const eventType = await this.getEventTypeByUsernameAndSlug({
|
||||
username,
|
||||
@@ -255,6 +256,7 @@ export class EventTypesService_2024_06_14 {
|
||||
orgSlug,
|
||||
orgId,
|
||||
authUser,
|
||||
sortCreatedAt,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -264,7 +266,7 @@ export class EventTypesService_2024_06_14 {
|
||||
}
|
||||
|
||||
if (authUser?.id) {
|
||||
return await this.getUserEventTypes(authUser.id);
|
||||
return await this.getUserEventTypes(authUser.id, sortCreatedAt);
|
||||
}
|
||||
|
||||
return [];
|
||||
|
||||
+31
-4
@@ -39,6 +39,7 @@ import {
|
||||
InputBookingField_2024_06_14,
|
||||
OutputUnknownLocation_2024_06_14,
|
||||
UpdateEventTypeInput_2024_06_14,
|
||||
supportedIntegrations,
|
||||
} from "@calcom/platform-types";
|
||||
import { BookerLayouts } from "@calcom/prisma/zod-utils";
|
||||
|
||||
@@ -542,14 +543,40 @@ export class InputEventTypesService_2024_06_14 {
|
||||
}
|
||||
|
||||
async checkAppIsValidAndConnected(user: UserWithProfile, appSlug: string) {
|
||||
const conferencingApps = ["google-meet", "office365-video", "zoom"];
|
||||
const conferencingApps = supportedIntegrations as readonly string[];
|
||||
if (!conferencingApps.includes(appSlug)) {
|
||||
throw new BadRequestException("Invalid app, available apps are: ", conferencingApps.join(", "));
|
||||
}
|
||||
|
||||
if (appSlug === "office365-video") {
|
||||
appSlug = "msteams";
|
||||
}
|
||||
// Map API integration names to actual app slugs
|
||||
const slugMap: Record<string, string> = {
|
||||
"office365-video": "msteams",
|
||||
"facetime-video": "facetime",
|
||||
"whereby-video": "whereby",
|
||||
"whatsapp-video": "whatsapp",
|
||||
"webex-video": "webex",
|
||||
"telegram-video": "telegram",
|
||||
"sylaps-video": "sylapsvideo",
|
||||
"skype-video": "skype",
|
||||
"sirius-video": "sirius_video",
|
||||
"signal-video": "signal",
|
||||
"shimmer-video": "shimmervideo",
|
||||
"salesroom-video": "salesroom",
|
||||
"roam-video": "roam",
|
||||
"riverside-video": "riverside",
|
||||
"ping-video": "ping",
|
||||
"mirotalk-video": "mirotalk",
|
||||
"jelly-video": "jelly",
|
||||
"jelly-conferencing": "jelly",
|
||||
"huddle": "huddle01",
|
||||
"element-call-video": "element-call",
|
||||
"eightxeight-video": "eightxeight",
|
||||
"discord-video": "discord",
|
||||
"demodesk-video": "demodesk",
|
||||
"campfire-video": "campfire",
|
||||
};
|
||||
|
||||
appSlug = slugMap[appSlug] || appSlug;
|
||||
|
||||
const credentials = await getUsersCredentialsIncludeServiceAccountKey(user);
|
||||
|
||||
|
||||
+17
-8
@@ -47,8 +47,8 @@ import { ERROR_STATUS, SUCCESS_STATUS } from "@calcom/platform-constants";
|
||||
import { handleCreatePhoneCall } from "@calcom/platform-libraries";
|
||||
import {
|
||||
CreateTeamEventTypeInput_2024_06_14,
|
||||
GetOrganizationEventTypesQuery_2024_06_14,
|
||||
GetTeamEventTypesQuery_2024_06_14,
|
||||
SkipTakePagination,
|
||||
TeamEventTypeOutput_2024_06_14,
|
||||
UpdateTeamEventTypeInput_2024_06_14,
|
||||
} from "@calcom/platform-types";
|
||||
@@ -159,12 +159,16 @@ export class OrganizationsEventTypesController {
|
||||
|
||||
@UseGuards(IsOrgGuard, IsTeamInOrg, IsAdminAPIEnabledGuard)
|
||||
@Get("/teams/:teamId/event-types")
|
||||
@ApiOperation({ summary: "Get team event types" })
|
||||
@ApiOperation({
|
||||
summary: "Get team event types",
|
||||
description:
|
||||
'Use the optional `sortCreatedAt` query parameter to order results by creation date (by ID). Accepts "asc" (oldest first) or "desc" (newest first). When not provided, no explicit ordering is applied.',
|
||||
})
|
||||
async getTeamEventTypes(
|
||||
@Param("teamId", ParseIntPipe) teamId: number,
|
||||
@Query() queryParams: GetTeamEventTypesQuery_2024_06_14
|
||||
): Promise<GetTeamEventTypesOutput> {
|
||||
const { eventSlug, hostsLimit } = queryParams;
|
||||
const { eventSlug, hostsLimit, sortCreatedAt } = queryParams;
|
||||
|
||||
if (eventSlug) {
|
||||
const eventType = await this.organizationsEventTypesService.getTeamEventTypeBySlug(
|
||||
@@ -179,7 +183,7 @@ export class OrganizationsEventTypesController {
|
||||
};
|
||||
}
|
||||
|
||||
const eventTypes = await this.organizationsEventTypesService.getTeamEventTypes(teamId);
|
||||
const eventTypes = await this.organizationsEventTypesService.getTeamEventTypes(teamId, sortCreatedAt);
|
||||
|
||||
return {
|
||||
status: SUCCESS_STATUS,
|
||||
@@ -191,16 +195,21 @@ export class OrganizationsEventTypesController {
|
||||
@PlatformPlan("ESSENTIALS")
|
||||
@UseGuards(ApiAuthGuard, IsOrgGuard, RolesGuard, PlatformPlanGuard, IsAdminAPIEnabledGuard)
|
||||
@Get("/teams/event-types")
|
||||
@ApiOperation({ summary: "Get all team event types" })
|
||||
@ApiOperation({
|
||||
summary: "Get all team event types",
|
||||
description:
|
||||
'Use the optional `sortCreatedAt` query parameter to order results by creation date (by ID). Accepts "asc" (oldest first) or "desc" (newest first). When not provided, no explicit ordering is applied.',
|
||||
})
|
||||
async getTeamsEventTypes(
|
||||
@Param("orgId", ParseIntPipe) orgId: number,
|
||||
@Query() queryParams: SkipTakePagination
|
||||
@Query() queryParams: GetOrganizationEventTypesQuery_2024_06_14
|
||||
): Promise<GetTeamEventTypesOutput> {
|
||||
const { skip, take } = queryParams;
|
||||
const { skip, take, sortCreatedAt } = queryParams;
|
||||
const eventTypes = await this.organizationsEventTypesService.getOrganizationsTeamsEventTypes(
|
||||
orgId,
|
||||
skip,
|
||||
take
|
||||
take,
|
||||
sortCreatedAt
|
||||
);
|
||||
|
||||
return {
|
||||
|
||||
+9
-1
@@ -1,16 +1,24 @@
|
||||
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
|
||||
import { Injectable } from "@nestjs/common";
|
||||
|
||||
import type { SortOrderType } from "@calcom/platform-types";
|
||||
|
||||
@Injectable()
|
||||
export class OrganizationsEventTypesRepository {
|
||||
constructor(private readonly dbRead: PrismaReadService) {}
|
||||
async getOrganizationTeamsEventTypes(orgId: number, skip: number, take: number) {
|
||||
async getOrganizationTeamsEventTypes(
|
||||
orgId: number,
|
||||
skip: number,
|
||||
take: number,
|
||||
sortCreatedAt?: SortOrderType
|
||||
) {
|
||||
return this.dbRead.prisma.eventType.findMany({
|
||||
where: {
|
||||
team: {
|
||||
parentId: orgId,
|
||||
},
|
||||
},
|
||||
...(sortCreatedAt && { orderBy: { id: sortCreatedAt } }),
|
||||
skip,
|
||||
take,
|
||||
include: { users: true, schedule: true, hosts: true, destinationCalendar: true },
|
||||
|
||||
+12
-4
@@ -1,4 +1,6 @@
|
||||
import { MembershipsRepository } from "@/modules/memberships/memberships.repository";
|
||||
|
||||
import type { SortOrderType } from "@calcom/platform-types";
|
||||
import { OrganizationsEventTypesRepository } from "@/modules/organizations/event-types/organizations-event-types.repository";
|
||||
import {
|
||||
TransformedCreateTeamEventTypeInput,
|
||||
@@ -83,16 +85,22 @@ export class OrganizationsEventTypesService {
|
||||
return this.teamsEventTypesService.getTeamEventTypeBySlug(teamId, eventTypeSlug, hostsLimit);
|
||||
}
|
||||
|
||||
async getTeamEventTypes(teamId: number): Promise<DatabaseTeamEventType[]> {
|
||||
return await this.teamsEventTypesService.getTeamEventTypes(teamId);
|
||||
async getTeamEventTypes(teamId: number, sortCreatedAt?: SortOrderType): Promise<DatabaseTeamEventType[]> {
|
||||
return await this.teamsEventTypesService.getTeamEventTypes(teamId, sortCreatedAt);
|
||||
}
|
||||
|
||||
async getOrganizationsTeamsEventTypes(
|
||||
orgId: number,
|
||||
skip = 0,
|
||||
take = 250
|
||||
take = 250,
|
||||
sortCreatedAt?: SortOrderType
|
||||
): Promise<DatabaseTeamEventType[]> {
|
||||
return await this.organizationEventTypesRepository.getOrganizationTeamsEventTypes(orgId, skip, take);
|
||||
return await this.organizationEventTypesRepository.getOrganizationTeamsEventTypes(
|
||||
orgId,
|
||||
skip,
|
||||
take,
|
||||
sortCreatedAt
|
||||
);
|
||||
}
|
||||
|
||||
async updateOrganizationTeamEventType(
|
||||
|
||||
+7
-3
@@ -136,12 +136,16 @@ export class TeamsEventTypesController {
|
||||
}
|
||||
|
||||
@Get("/")
|
||||
@ApiOperation({ summary: "Get a team event type" })
|
||||
@ApiOperation({
|
||||
summary: "Get team event types",
|
||||
description:
|
||||
'Use the optional `sortCreatedAt` query parameter to order results by creation date (by ID). Accepts "asc" (oldest first) or "desc" (newest first). When not provided, no explicit ordering is applied.',
|
||||
})
|
||||
async getTeamEventTypes(
|
||||
@Param("teamId", ParseIntPipe) teamId: number,
|
||||
@Query() queryParams: GetTeamEventTypesQuery_2024_06_14
|
||||
): Promise<GetTeamEventTypesOutput> {
|
||||
const { eventSlug, hostsLimit } = queryParams;
|
||||
const { eventSlug, hostsLimit, sortCreatedAt } = queryParams;
|
||||
|
||||
if (eventSlug) {
|
||||
const eventType = await this.teamsEventTypesService.getTeamEventTypeBySlug(
|
||||
@@ -156,7 +160,7 @@ export class TeamsEventTypesController {
|
||||
};
|
||||
}
|
||||
|
||||
const eventTypes = await this.teamsEventTypesService.getTeamEventTypes(teamId);
|
||||
const eventTypes = await this.teamsEventTypesService.getTeamEventTypes(teamId, sortCreatedAt);
|
||||
|
||||
return {
|
||||
status: SUCCESS_STATUS,
|
||||
|
||||
@@ -11,6 +11,8 @@ import { UsersService } from "@/modules/users/services/users.service";
|
||||
import { UserWithProfile } from "@/modules/users/users.repository";
|
||||
import { Injectable, NotFoundException, Logger } from "@nestjs/common";
|
||||
|
||||
import type { SortOrderType } from "@calcom/platform-types";
|
||||
|
||||
import { createEventType, updateEventType } from "@calcom/platform-libraries/event-types";
|
||||
|
||||
@Injectable()
|
||||
@@ -101,8 +103,8 @@ export class TeamsEventTypesService {
|
||||
return eventType;
|
||||
}
|
||||
|
||||
async getTeamEventTypes(teamId: number): Promise<DatabaseTeamEventType[]> {
|
||||
return await this.teamsEventTypesRepository.getTeamEventTypes(teamId);
|
||||
async getTeamEventTypes(teamId: number, sortCreatedAt?: SortOrderType): Promise<DatabaseTeamEventType[]> {
|
||||
return await this.teamsEventTypesRepository.getTeamEventTypes(teamId, sortCreatedAt);
|
||||
}
|
||||
|
||||
async updateTeamEventType(
|
||||
|
||||
@@ -2,6 +2,8 @@ import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
|
||||
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
|
||||
import { Injectable } from "@nestjs/common";
|
||||
|
||||
import type { SortOrderType } from "@calcom/platform-types";
|
||||
|
||||
@Injectable()
|
||||
export class TeamsEventTypesRepository {
|
||||
constructor(private readonly dbRead: PrismaReadService, private readonly dbWrite: PrismaWriteService) {}
|
||||
@@ -79,11 +81,12 @@ export class TeamsEventTypesRepository {
|
||||
});
|
||||
}
|
||||
|
||||
async getTeamEventTypes(teamId: number) {
|
||||
async getTeamEventTypes(teamId: number, sortCreatedAt?: SortOrderType) {
|
||||
return this.dbRead.prisma.eventType.findMany({
|
||||
where: {
|
||||
teamId,
|
||||
},
|
||||
...(sortCreatedAt && { orderBy: { id: sortCreatedAt } }),
|
||||
include: {
|
||||
users: true,
|
||||
schedule: true,
|
||||
|
||||
@@ -115,7 +115,7 @@ export function UserPage(props: PageProps) {
|
||||
eventType: type,
|
||||
});
|
||||
}}
|
||||
className="bg-default border-subtle dark:bg-cal-muted dark:hover:bg-emphasis hover:bg-cal-muted group relative border-b transition first:rounded-t-md last:rounded-b-md last:border-b-0"
|
||||
className="bg-default border-subtle dark:bg-cal-muted dark:hover:bg-subtle hover:bg-cal-muted group relative border-b transition first:rounded-t-md last:rounded-b-md last:border-b-0"
|
||||
data-testid="event-type-link">
|
||||
<Icon
|
||||
name="arrow-right"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@calcom/web",
|
||||
"version": "5.9.9",
|
||||
"version": "5.9.10",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"analyze": "ANALYZE=true next build",
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
import { createReadStream, existsSync } from "fs";
|
||||
import { promises as fs } from "fs";
|
||||
import { IncomingMessage, ServerResponse } from "http";
|
||||
import { extname, join, normalize } from "path";
|
||||
|
||||
const distDir = join(process.cwd(), "dist");
|
||||
|
||||
const mimeTypes: Record<string, string> = {
|
||||
".html": "text/html; charset=utf-8",
|
||||
".js": "application/javascript; charset=utf-8",
|
||||
".css": "text/css; charset=utf-8",
|
||||
".json": "application/json; charset=utf-8",
|
||||
".png": "image/png",
|
||||
".jpg": "image/jpeg",
|
||||
".jpeg": "image/jpeg",
|
||||
".svg": "image/svg+xml",
|
||||
".ico": "image/x-icon",
|
||||
".woff": "font/woff",
|
||||
".woff2": "font/woff2",
|
||||
".ttf": "font/ttf",
|
||||
".map": "application/json; charset=utf-8",
|
||||
};
|
||||
|
||||
const getSafePath = (urlPath: string): string => {
|
||||
const decodedPath = decodeURIComponent(urlPath.split("?")[0]);
|
||||
const normalized = normalize(decodedPath).replace(/^(\.\.(\/|\\|$))+/, "");
|
||||
return normalized.startsWith("/") ? normalized : `/${normalized}`;
|
||||
};
|
||||
|
||||
const resolveFilePath = async (safePath: string): Promise<string> => {
|
||||
let candidate = join(distDir, safePath);
|
||||
try {
|
||||
const stats = await fs.stat(candidate);
|
||||
if (stats.isDirectory()) {
|
||||
candidate = join(candidate, "index.html");
|
||||
}
|
||||
return candidate;
|
||||
} catch {
|
||||
const fallback = join(distDir, "index.html");
|
||||
if (existsSync(fallback)) {
|
||||
return fallback;
|
||||
}
|
||||
throw new Error("Dist folder missing. Run `npm run build` before deploying.");
|
||||
}
|
||||
};
|
||||
|
||||
const sendFile = (res: ServerResponse, filePath: string) => {
|
||||
const ext = extname(filePath).toLowerCase();
|
||||
const contentType = mimeTypes[ext] || "application/octet-stream";
|
||||
res.setHeader("Content-Type", contentType);
|
||||
res.setHeader(
|
||||
"Cache-Control",
|
||||
ext === ".html" ? "no-cache, no-store, must-revalidate" : "public, max-age=31536000, immutable"
|
||||
);
|
||||
|
||||
const stream = createReadStream(filePath);
|
||||
stream.on("error", (error) => {
|
||||
console.error("Failed to read file:", error);
|
||||
res.statusCode = 500;
|
||||
res.end("Internal Server Error");
|
||||
});
|
||||
stream.pipe(res);
|
||||
};
|
||||
|
||||
export default async function handler(req: IncomingMessage, res: ServerResponse) {
|
||||
if (req.method && !["GET", "HEAD"].includes(req.method)) {
|
||||
res.statusCode = 405;
|
||||
res.setHeader("Allow", "GET, HEAD");
|
||||
res.end("Method Not Allowed");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const urlPath = req.url ?? "/";
|
||||
const safePath = getSafePath(urlPath);
|
||||
const filePath = await resolveFilePath(safePath);
|
||||
sendFile(res, filePath);
|
||||
} catch (error) {
|
||||
console.error("Server render error:", error);
|
||||
res.statusCode = 500;
|
||||
res.end("Internal Server Error");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"routes": [
|
||||
{
|
||||
"handle": "filesystem"
|
||||
},
|
||||
{
|
||||
"src": "/.*",
|
||||
"dest": "/index.html"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -2819,6 +2819,7 @@
|
||||
"organization.attributes.update",
|
||||
"organization.attributes.delete",
|
||||
"organization.attributes.create",
|
||||
"organization.attributes.editUsers",
|
||||
"routingForm.create",
|
||||
"routingForm.read",
|
||||
"routingForm.update",
|
||||
@@ -4470,6 +4471,7 @@
|
||||
"get": {
|
||||
"operationId": "OrganizationsEventTypesController_getTeamEventTypes",
|
||||
"summary": "Get team event types",
|
||||
"description": "Use the optional `sortCreatedAt` query parameter to order results by creation date (by ID). Accepts \"asc\" (oldest first) or \"desc\" (newest first). When not provided, no explicit ordering is applied.",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
@@ -4523,6 +4525,16 @@
|
||||
"schema": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "sortCreatedAt",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"description": "Sort event types by creation date. When not provided, no explicit ordering is applied.",
|
||||
"schema": {
|
||||
"enum": ["asc", "desc"],
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@@ -4816,6 +4828,7 @@
|
||||
"get": {
|
||||
"operationId": "OrganizationsEventTypesController_getTeamsEventTypes",
|
||||
"summary": "Get all team event types",
|
||||
"description": "Use the optional `sortCreatedAt` query parameter to order results by creation date (by ID). Accepts \"asc\" (oldest first) or \"desc\" (newest first). When not provided, no explicit ordering is applied.",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
@@ -4876,6 +4889,16 @@
|
||||
"default": 0,
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "sortCreatedAt",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"description": "Sort event types by creation date. When not provided, no explicit ordering is applied.",
|
||||
"schema": {
|
||||
"enum": ["asc", "desc"],
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@@ -11552,7 +11575,7 @@
|
||||
"get": {
|
||||
"operationId": "EventTypesController_2024_06_14_getEventTypes",
|
||||
"summary": "Get all event types",
|
||||
"description": "Hidden event types are returned only if authentication is provided and it belongs to the event type owner.\n \n <Note>Please make sure to pass in the cal-api-version header value as mentioned in the Headers section. Not passing the correct value will default to an older version of this endpoint.</Note>\n ",
|
||||
"description": "Hidden event types are returned only if authentication is provided and it belongs to the event type owner.\n \n Use the optional `sortCreatedAt` query parameter to order results by creation date (by ID). Accepts \"asc\" (oldest first) or \"desc\" (newest first). When not provided, no explicit ordering is applied.\n \n <Note>Please make sure to pass in the cal-api-version header value as mentioned in the Headers section. Not passing the correct value will default to an older version of this endpoint.</Note>\n ",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "cal-api-version",
|
||||
@@ -11609,6 +11632,16 @@
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "sortCreatedAt",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"description": "Sort event types by creation date. When not provided, no explicit ordering is applied.",
|
||||
"schema": {
|
||||
"enum": ["asc", "desc"],
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Authorization",
|
||||
"in": "header",
|
||||
@@ -14420,6 +14453,7 @@
|
||||
"get": {
|
||||
"operationId": "TeamsEventTypesController_getTeamEventTypes",
|
||||
"summary": "Get a team event type",
|
||||
"description": "Use the optional `sortCreatedAt` query parameter to order results by creation date (by ID). Accepts \"asc\" (oldest first) or \"desc\" (newest first). When not provided, no explicit ordering is applied.",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "teamId",
|
||||
@@ -14446,6 +14480,16 @@
|
||||
"schema": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "sortCreatedAt",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"description": "Sort event types by creation date. When not provided, no explicit ordering is applied.",
|
||||
"schema": {
|
||||
"enum": ["asc", "desc"],
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@@ -16781,7 +16825,37 @@
|
||||
"integration": {
|
||||
"type": "string",
|
||||
"example": "cal-video",
|
||||
"enum": ["cal-video", "google-meet", "office365-video", "zoom"]
|
||||
"enum": [
|
||||
"cal-video",
|
||||
"google-meet",
|
||||
"zoom",
|
||||
"whereby-video",
|
||||
"whatsapp-video",
|
||||
"webex-video",
|
||||
"telegram-video",
|
||||
"tandem",
|
||||
"sylaps-video",
|
||||
"skype-video",
|
||||
"sirius-video",
|
||||
"signal-video",
|
||||
"shimmer-video",
|
||||
"salesroom-video",
|
||||
"roam-video",
|
||||
"riverside-video",
|
||||
"ping-video",
|
||||
"office365-video",
|
||||
"mirotalk-video",
|
||||
"jitsi",
|
||||
"jelly-video",
|
||||
"jelly-conferencing",
|
||||
"huddle",
|
||||
"facetime-video",
|
||||
"element-call-video",
|
||||
"eightxeight-video",
|
||||
"discord-video",
|
||||
"demodesk-video",
|
||||
"campfire-video"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": ["type", "integration"]
|
||||
@@ -17989,7 +18063,7 @@
|
||||
},
|
||||
"locations": {
|
||||
"type": "array",
|
||||
"description": "Locations where the event will take place. If not provided, cal video link will be used as the location.",
|
||||
"description": "Locations where the event will take place. If not provided, cal video link will be used as the location. Note: Setting a location to a conferencing app does not install the app - the app must already be installed. Via API, only Google Meet (google-meet), Microsoft Teams (office365-video), and Zoom (zoom) can be installed. Cal Video (cal-video) is installed by default. All other conferencing apps must be connected via the Cal.com web app and are not available for Platform plan customers. You can only set an event type location to an app that has already been installed or connected.",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
@@ -19920,7 +19994,7 @@
|
||||
},
|
||||
"locations": {
|
||||
"type": "array",
|
||||
"description": "Locations where the event will take place. If not provided, cal video link will be used as the location.",
|
||||
"description": "Locations where the event will take place. If not provided, cal video link will be used as the location. Note: Setting a location to a conferencing app does not install the app - the app must already be installed. Via API, only Google Meet (google-meet), Microsoft Teams (office365-video), and Zoom (zoom) can be installed. Cal Video (cal-video) is installed by default. All other conferencing apps must be connected via the Cal.com web app and are not available for Platform plan customers. You can only set an event type location to an app that has already been installed or connected.",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
@@ -21561,7 +21635,7 @@
|
||||
},
|
||||
"locations": {
|
||||
"type": "array",
|
||||
"description": "Locations where the event will take place. If not provided, cal video link will be used as the location.",
|
||||
"description": "Locations where the event will take place. If not provided, cal video link will be used as the location. Note: Setting a location to a conferencing app does not install the app - the app must already be installed. Via API, only Google Meet (google-meet), Microsoft Teams (office365-video), and Zoom (zoom) can be installed. Cal Video (cal-video) is installed by default. All other conferencing apps must be connected via the Cal.com web app and are not available for Platform plan customers. You can only set an event type location to an app that has already been installed or connected.",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
@@ -22024,7 +22098,7 @@
|
||||
},
|
||||
"locations": {
|
||||
"type": "array",
|
||||
"description": "Locations where the event will take place. If not provided, cal video link will be used as the location.",
|
||||
"description": "Locations where the event will take place. If not provided, cal video link will be used as the location. Note: Setting a location to a conferencing app does not install the app - the app must already be installed. Via API, only Google Meet (google-meet), Microsoft Teams (office365-video), and Zoom (zoom) can be installed. Cal Video (cal-video) is installed by default. All other conferencing apps must be connected via the Cal.com web app and are not available for Platform plan customers. You can only set an event type location to an app that has already been installed or connected.",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
@@ -26294,6 +26368,7 @@
|
||||
"organization.attributes.update",
|
||||
"organization.attributes.delete",
|
||||
"organization.attributes.create",
|
||||
"organization.attributes.editUsers",
|
||||
"routingForm.create",
|
||||
"routingForm.read",
|
||||
"routingForm.update",
|
||||
@@ -26397,6 +26472,7 @@
|
||||
"organization.attributes.update",
|
||||
"organization.attributes.delete",
|
||||
"organization.attributes.create",
|
||||
"organization.attributes.editUsers",
|
||||
"routingForm.create",
|
||||
"routingForm.read",
|
||||
"routingForm.update",
|
||||
@@ -26528,6 +26604,7 @@
|
||||
"organization.attributes.update",
|
||||
"organization.attributes.delete",
|
||||
"organization.attributes.create",
|
||||
"organization.attributes.editUsers",
|
||||
"routingForm.create",
|
||||
"routingForm.read",
|
||||
"routingForm.update",
|
||||
@@ -26630,6 +26707,7 @@
|
||||
"organization.attributes.update",
|
||||
"organization.attributes.delete",
|
||||
"organization.attributes.create",
|
||||
"organization.attributes.editUsers",
|
||||
"routingForm.create",
|
||||
"routingForm.read",
|
||||
"routingForm.update",
|
||||
@@ -27467,6 +27545,14 @@
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"avatarUrl": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"bio": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"timeFormat": {
|
||||
"type": "number"
|
||||
},
|
||||
@@ -27493,6 +27579,8 @@
|
||||
"username",
|
||||
"email",
|
||||
"name",
|
||||
"avatarUrl",
|
||||
"bio",
|
||||
"timeFormat",
|
||||
"defaultScheduleId",
|
||||
"weekStart",
|
||||
|
||||
@@ -381,7 +381,7 @@ export const CreateOrEditOutOfOfficeEntryModal = ({
|
||||
onChange={(e) => setSearchRedirectMember(e.target.value)}
|
||||
value={searchRedirectMember}
|
||||
/>
|
||||
<div className="scroll-bar bg-default mt-2 flex h-[150px] flex-col gap-0.5 overflow-y-scroll rounded-[10px] border p-1">
|
||||
<div className="scroll-bar bg-default mt-2 flex h-[150px] flex-col gap-0.5 overflow-y-scroll rounded-[10px] border p-2 pl-5">
|
||||
{redirectToMemberListOptions
|
||||
.filter((member) => member.value !== getValues("forUserId"))
|
||||
.map((member) => (
|
||||
|
||||
+2
-2
@@ -518,7 +518,7 @@ export class CreateEventTypeInput_2024_06_14 extends BaseCreateEventTypeInput {
|
||||
@ValidateLocations_2024_06_14()
|
||||
@DocsPropertyOptional({
|
||||
description:
|
||||
"Locations where the event will take place. If not provided, cal video link will be used as the location.",
|
||||
"Locations where the event will take place. If not provided, cal video link will be used as the location. Note: Setting a location to a conferencing app does not install the app - the app must already be installed. Via API, only Google Meet (google-meet), Microsoft Teams (office365-video), and Zoom (zoom) can be installed. Cal Video (cal-video) is installed by default. All other conferencing apps must be connected via the Cal.com web app and are not available for Platform plan customers. You can only set an event type location to an app that has already been installed or connected.",
|
||||
oneOf: [
|
||||
{ $ref: getSchemaPath(InputAddressLocation_2024_06_14) },
|
||||
{ $ref: getSchemaPath(InputLinkLocation_2024_06_14) },
|
||||
@@ -607,7 +607,7 @@ export class CreateTeamEventTypeInput_2024_06_14 extends BaseCreateEventTypeInpu
|
||||
@ValidateTeamLocations_2024_06_14()
|
||||
@DocsPropertyOptional({
|
||||
description:
|
||||
"Locations where the event will take place. If not provided, cal video link will be used as the location.",
|
||||
"Locations where the event will take place. If not provided, cal video link will be used as the location. Note: Setting a location to a conferencing app does not install the app - the app must already be installed. Via API, only Google Meet (google-meet), Microsoft Teams (office365-video), and Zoom (zoom) can be installed. Cal Video (cal-video) is installed by default. All other conferencing apps must be connected via the Cal.com web app and are not available for Platform plan customers. You can only set an event type location to an app that has already been installed or connected.",
|
||||
oneOf: [
|
||||
{ $ref: getSchemaPath(InputAddressLocation_2024_06_14) },
|
||||
{ $ref: getSchemaPath(InputLinkLocation_2024_06_14) },
|
||||
|
||||
+29
-1
@@ -1,6 +1,8 @@
|
||||
import { ApiPropertyOptional } from "@nestjs/swagger";
|
||||
import { Transform } from "class-transformer";
|
||||
import { IsNumber, IsOptional, IsString } from "class-validator";
|
||||
import { IsEnum, IsNumber, IsOptional, IsString } from "class-validator";
|
||||
|
||||
import { SkipTakePagination, SortOrder, SortOrderType } from "../../../pagination/pagination.input";
|
||||
|
||||
export class GetEventTypesQuery_2024_06_14 {
|
||||
@IsOptional()
|
||||
@@ -46,6 +48,14 @@ export class GetEventTypesQuery_2024_06_14 {
|
||||
type: Number,
|
||||
})
|
||||
orgId?: number;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
enum: SortOrder,
|
||||
description: "Sort event types by creation date. When not provided, no explicit ordering is applied.",
|
||||
})
|
||||
@IsOptional()
|
||||
@IsEnum(SortOrder)
|
||||
sortCreatedAt?: SortOrderType;
|
||||
}
|
||||
|
||||
export class GetTeamEventTypesQuery_2024_06_14 {
|
||||
@@ -64,6 +74,24 @@ export class GetTeamEventTypesQuery_2024_06_14 {
|
||||
"Specifies the maximum number of hosts to include in the response. This limit helps optimize performance. If not provided, all Hosts will be fetched.",
|
||||
})
|
||||
hostsLimit?: number;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
enum: SortOrder,
|
||||
description: "Sort event types by creation date. When not provided, no explicit ordering is applied.",
|
||||
})
|
||||
@IsOptional()
|
||||
@IsEnum(SortOrder)
|
||||
sortCreatedAt?: SortOrderType;
|
||||
}
|
||||
|
||||
export class GetOrganizationEventTypesQuery_2024_06_14 extends SkipTakePagination {
|
||||
@ApiPropertyOptional({
|
||||
enum: SortOrder,
|
||||
description: "Sort event types by creation date. When not provided, no explicit ordering is applied.",
|
||||
})
|
||||
@IsOptional()
|
||||
@IsEnum(SortOrder)
|
||||
sortCreatedAt?: SortOrderType;
|
||||
}
|
||||
|
||||
function TransformUsernames() {
|
||||
|
||||
+31
-1
@@ -54,7 +54,37 @@ export class InputLinkLocation_2024_06_14 {
|
||||
public!: boolean;
|
||||
}
|
||||
|
||||
export const supportedIntegrations = ["cal-video", "google-meet", "office365-video", "zoom"] as const;
|
||||
export const supportedIntegrations = [
|
||||
"cal-video",
|
||||
"google-meet",
|
||||
"zoom",
|
||||
"whereby-video",
|
||||
"whatsapp-video",
|
||||
"webex-video",
|
||||
"telegram-video",
|
||||
"tandem",
|
||||
"sylaps-video",
|
||||
"skype-video",
|
||||
"sirius-video",
|
||||
"signal-video",
|
||||
"shimmer-video",
|
||||
"salesroom-video",
|
||||
"roam-video",
|
||||
"riverside-video",
|
||||
"ping-video",
|
||||
"office365-video",
|
||||
"mirotalk-video",
|
||||
"jitsi",
|
||||
"jelly-video",
|
||||
"jelly-conferencing",
|
||||
"huddle",
|
||||
"facetime-video",
|
||||
"element-call-video",
|
||||
"eightxeight-video",
|
||||
"discord-video",
|
||||
"demodesk-video",
|
||||
"campfire-video",
|
||||
] as const;
|
||||
export type Integration_2024_06_14 = (typeof supportedIntegrations)[number];
|
||||
|
||||
export class InputIntegrationLocation_2024_06_14 {
|
||||
|
||||
+2
-2
@@ -462,7 +462,7 @@ export class UpdateEventTypeInput_2024_06_14 extends BaseUpdateEventTypeInput {
|
||||
@ValidateLocations_2024_06_14()
|
||||
@DocsPropertyOptional({
|
||||
description:
|
||||
"Locations where the event will take place. If not provided, cal video link will be used as the location.",
|
||||
"Locations where the event will take place. If not provided, cal video link will be used as the location. Note: Setting a location to a conferencing app does not install the app - the app must already be installed. Via API, only Google Meet (google-meet), Microsoft Teams (office365-video), and Zoom (zoom) can be installed. Cal Video (cal-video) is installed by default. All other conferencing apps must be connected via the Cal.com web app and are not available for Platform plan customers. You can only set an event type location to an app that has already been installed or connected.",
|
||||
oneOf: [
|
||||
{ $ref: getSchemaPath(InputAddressLocation_2024_06_14) },
|
||||
{ $ref: getSchemaPath(InputLinkLocation_2024_06_14) },
|
||||
@@ -521,7 +521,7 @@ export class UpdateTeamEventTypeInput_2024_06_14 extends BaseUpdateEventTypeInpu
|
||||
@ValidateTeamLocations_2024_06_14()
|
||||
@DocsPropertyOptional({
|
||||
description:
|
||||
"Locations where the event will take place. If not provided, cal video link will be used as the location.",
|
||||
"Locations where the event will take place. If not provided, cal video link will be used as the location. Note: Setting a location to a conferencing app does not install the app - the app must already be installed. Via API, only Google Meet (google-meet), Microsoft Teams (office365-video), and Zoom (zoom) can be installed. Cal Video (cal-video) is installed by default. All other conferencing apps must be connected via the Cal.com web app and are not available for Platform plan customers. You can only set an event type location to an app that has already been installed or connected.",
|
||||
oneOf: [
|
||||
{ $ref: getSchemaPath(InputAddressLocation_2024_06_14) },
|
||||
{ $ref: getSchemaPath(InputLinkLocation_2024_06_14) },
|
||||
|
||||
@@ -2,6 +2,13 @@ import { ApiPropertyOptional } from "@nestjs/swagger";
|
||||
import { Transform } from "class-transformer";
|
||||
import { IsNumber, Min, Max, IsOptional } from "class-validator";
|
||||
|
||||
export enum SortOrder {
|
||||
ASC = "asc",
|
||||
DESC = "desc",
|
||||
}
|
||||
|
||||
export type SortOrderType = `${SortOrder}`;
|
||||
|
||||
export class Pagination {
|
||||
@ApiPropertyOptional({ description: "The number of items to return", example: 10 })
|
||||
@Transform(({ value }: { value: string }) => value && parseInt(value))
|
||||
|
||||
@@ -22,10 +22,6 @@ export function InputLeading(props: JSX.IntrinsicElements["div"]) {
|
||||
);
|
||||
}
|
||||
|
||||
type PasswordFieldTranslations = {
|
||||
showPasswordText?: string;
|
||||
hidePasswordText?: string;
|
||||
};
|
||||
|
||||
export const PasswordField = forwardRef<HTMLInputElement, InputFieldProps>(function PasswordField(
|
||||
props,
|
||||
@@ -194,7 +190,7 @@ export const FilterSearchField = forwardRef<HTMLInputElement, InputFieldProps>(f
|
||||
ref={ref}
|
||||
addOnLeading={<Icon name="search" className="h-4 w-4 stroke-[2.5px]" data-testid="search-icon" />}
|
||||
placeholder="Search"
|
||||
containerClassName="mx-3 mt-2"
|
||||
containerClassName="mt-1"
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user