Skip to content
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { getEventTypeById } from "@calcom/platform-libraries/event-types";
import type { PrismaClient } from "@calcom/prisma";
import { Injectable } from "@nestjs/common";
import { CreateEventTypeInput_2024_04_15 } from "@/ee/event-types/event-types_2024_04_15/inputs/create-event-type.input";
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
import { UsersService } from "@/modules/users/services/users.service";
import { UserWithProfile } from "@/modules/users/users.repository";
import { Injectable } from "@nestjs/common";

import { getEventTypeById } from "@calcom/platform-libraries/event-types";
import type { PrismaClient } from "@calcom/prisma";

@Injectable()
export class EventTypesRepository_2024_04_15 {
Expand Down Expand Up @@ -54,6 +53,7 @@ export class EventTypesRepository_2024_04_15 {
currentOrganizationId: this.usersService.getUserMainOrgId(user),
eventTypeId,
userId: user.id,
userLocale: user.locale ?? "en",
prisma: this.dbRead.prisma as unknown as PrismaClient,
isUserOrganizationAdmin,
isTrpcCall: true,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,42 +1,43 @@
import { EventTypesService_2024_06_14 } from "@/ee/event-types/event-types_2024_06_14/services/event-types.service";
import { systemBeforeFieldEmail } from "@/ee/event-types/event-types_2024_06_14/transformers";
import { AtomsRepository } from "@/modules/atoms/atoms.repository";
import { CredentialsRepository } from "@/modules/credentials/credentials.repository";
import { MembershipsRepository } from "@/modules/memberships/memberships.repository";
import { OrganizationsTeamsRepository } from "@/modules/organizations/teams/index/organizations-teams.repository";
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
import { TeamsEventTypesService } from "@/modules/teams/event-types/services/teams-event-types.service";
import { UsersService } from "@/modules/users/services/users.service";
import { UserWithProfile } from "@/modules/users/users.repository";
import { Injectable, NotFoundException, ForbiddenException, BadRequestException } from "@nestjs/common";

import { checkAdminOrOwner, getClientSecretFromPayment } from "@calcom/platform-libraries";
import type { TeamQuery } from "@calcom/platform-libraries";
import { enrichUserWithDelegationConferencingCredentialsWithoutOrgId } from "@calcom/platform-libraries/app-store";
import { getEnabledAppsFromCredentials, getAppFromSlug } from "@calcom/platform-libraries/app-store";
import { checkAdminOrOwner, getClientSecretFromPayment } from "@calcom/platform-libraries";
import type {
App,
TDependencyData,
CredentialDataWithTeamName,
CredentialOwner,
CredentialPayload,
CredentialDataWithTeamName,
LocationOption,
TDependencyData,
} from "@calcom/platform-libraries/app-store";
import {
enrichUserWithDelegationConferencingCredentialsWithoutOrgId,
getAppFromSlug,
getEnabledAppsFromCredentials,
} from "@calcom/platform-libraries/app-store";
import { type PublicEventType, getPublicEvent } from "@calcom/platform-libraries/event-types";
import {
getEventTypeById,
bulkUpdateEventsToDefaultLocation,
bulkUpdateTeamEventsToDefaultLocation,
getBulkUserEventTypes,
EventTypeMetaDataSchema,
getBulkTeamEventTypes,
} from "@calcom/platform-libraries/event-types";
import {
updateEventType,
getBulkUserEventTypes,
getEventTypeById,
getPublicEvent,
type PublicEventType,
TUpdateEventTypeInputSchema,
EventTypeMetaDataSchema,
updateEventType,
} from "@calcom/platform-libraries/event-types";
import type { PrismaClient } from "@calcom/prisma";
import { BadRequestException, ForbiddenException, Injectable, NotFoundException } from "@nestjs/common";
import { EventTypesService_2024_06_14 } from "@/ee/event-types/event-types_2024_06_14/services/event-types.service";
import { systemBeforeFieldEmail } from "@/ee/event-types/event-types_2024_06_14/transformers";
import { AtomsRepository } from "@/modules/atoms/atoms.repository";
import { CredentialsRepository } from "@/modules/credentials/credentials.repository";
import { MembershipsRepository } from "@/modules/memberships/memberships.repository";
import { OrganizationsTeamsRepository } from "@/modules/organizations/teams/index/organizations-teams.repository";
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
import { TeamsEventTypesService } from "@/modules/teams/event-types/services/teams-event-types.service";
import { UsersService } from "@/modules/users/services/users.service";
import { UserWithProfile } from "@/modules/users/users.repository";

type EnabledAppType = App & {
credential: CredentialDataWithTeamName;
Expand Down Expand Up @@ -81,6 +82,7 @@ export class EventTypesAtomService {
currentOrganizationId: this.usersService.getUserMainOrgId(user),
eventTypeId,
userId: user.id,
userLocale: user.locale ?? "en",
prisma: this.dbRead.prisma as unknown as PrismaClient,
isUserOrganizationAdmin,
isTrpcCall: true,
Expand Down Expand Up @@ -136,8 +138,6 @@ export class EventTypesAtomService {
input: { ...body, id: eventTypeId, bookingFields },
ctx: {
user: eventTypeUser,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
prisma: this.dbWrite.prisma,
},
});
Expand Down Expand Up @@ -166,8 +166,6 @@ export class EventTypesAtomService {
input: { ...body, id: eventTypeId, bookingFields },
ctx: {
user: eventTypeUser,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
prisma: this.dbWrite.prisma,
},
});
Expand Down
1 change: 1 addition & 0 deletions apps/web/public/static/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -739,6 +739,7 @@
"booking_reschedule_confirmation": "Reschedule your {{eventTypeTitle}} with {{profileName}}",
"in_person_meeting": "In-person meeting",
"in_person": "In Person (Organizer Address)",
"in_person_category": "In person",
"link_meeting": "Link meeting",
"phone_number": "Phone number",
"attendee_phone_number": "Attendee phone number",
Expand Down
1 change: 1 addition & 0 deletions apps/web/public/static/locales/fr/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -719,6 +719,7 @@
"booking_reschedule_confirmation": "Replanifiez votre {{eventTypeTitle}} avec {{profileName}}",
"in_person_meeting": "Rendez-vous en personne",
"in_person": "En personne (adresse de l'organisateur)",
"in_person_category": "En personne",
"link_meeting": "Rendez-vous avec lien",
"phone_number": "Numéro de téléphone",
"attendee_phone_number": "Numéro de téléphone du participant",
Expand Down
22 changes: 11 additions & 11 deletions packages/app-store/locations.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
/**
* TODO: Consolidate this file with BookingLocationService and add tests
*/
import type { TFunction } from "i18next";
import { isValidPhoneNumber } from "libphonenumber-js/max";
import { z } from "zod";

import { appStoreMetadata } from "@calcom/app-store/bookerAppsMetaData";
import logger from "@calcom/lib/logger";
import { BookingStatus } from "@calcom/prisma/enums";
import type { Ensure, Optional } from "@calcom/types/utils";
import type { TFunction } from "i18next";
import { isValidPhoneNumber } from "libphonenumber-js/max";
import { z } from "zod";

import type { EventLocationTypeFromAppMeta } from "../types/App";
import {
DailyLocationType as importedDailyLocationType,
MeetLocationType as importedMeetLocationType,
MSTeamsLocationType as importedMSTeamsLocationType,
DailyLocationType as importedDailyLocationType,
} from "./constants";

export const MeetLocationType = importedMeetLocationType;
Expand All @@ -26,7 +26,7 @@ export type DefaultEventLocationType = {
type: DefaultEventLocationTypeEnum;
label: string;
messageForOrganizer: string;
category: "in person" | "conferencing" | "other" | "phone";
category: "in_person_category" | "conferencing" | "other" | "phone";
linkType: "static";
supportsCustomLabel?: boolean;

Expand Down Expand Up @@ -114,7 +114,7 @@ export const defaultLocations: DefaultEventLocationType[] = [
attendeeInputPlaceholder: "enter_address",
defaultValueVariable: "attendeeAddress",
iconUrl: "/map-pin-dark.svg",
category: "in person",
category: "in_person_category",
linkType: "static",
supportsCustomLabel: true,
},
Expand Down Expand Up @@ -143,7 +143,7 @@ export const defaultLocations: DefaultEventLocationType[] = [
variable: "locationAddress",
defaultValueVariable: "address",
iconUrl: "/map-pin-dark.svg",
category: "in person",
category: "in_person_category",
linkType: "static",
},
{
Expand Down Expand Up @@ -242,7 +242,7 @@ for (const [appName, meta] of Object.entries(appStoreMetadata)) {
for (const [key, value] of Object.entries(location)) {
if (typeof value === "string") {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
// @ts-expect-error
location[key] = value.replace(/{SLUG}/g, meta.slug).replace(/{TITLE}/g, meta.name);
}
}
Expand Down Expand Up @@ -397,7 +397,7 @@ export const getLocationValueForDB = (
eventLocations: LocationObject[]
) => {
let bookingLocation = bookingLocationTypeOrValue;
let conferenceCredentialId: number | undefined = undefined;
let conferenceCredentialId: number | undefined;

eventLocations.forEach((location) => {
if (location.type === bookingLocationTypeOrValue) {
Expand Down Expand Up @@ -481,8 +481,8 @@ export const getTranslatedLocation = (
const translatedLocation = location.type.startsWith("integrations:")
? eventLocationType.label
: translateAbleKeys.includes(locationKey)
? t(locationKey)
: locationKey;
? t(locationKey)
: locationKey;

return translatedLocation;
};
Expand Down
74 changes: 39 additions & 35 deletions packages/features/eventtypes/lib/getEventTypeById.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import { getLocationGroupedOptions } from "@calcom/app-store/server";
import { getEventTypeAppData } from "@calcom/app-store/utils";
import { eventTypeMetaDataSchemaWithTypedApps } from "@calcom/app-store/zod-utils";
import { getBookingFieldsWithSystemFields } from "@calcom/features/bookings/lib/getBookingFields";
import { getOrganizationRepository } from "@calcom/features/ee/organizations/di/OrganizationRepository.container";
import { getBookerBaseUrl } from "@calcom/features/ee/organizations/lib/getBookerUrlServer";
import { OrganizationRepository } from "@calcom/features/ee/organizations/repositories/OrganizationRepository";
import { EventTypeRepository } from "@calcom/features/eventtypes/repositories/eventTypeRepository";
import { UserRepository } from "@calcom/features/users/repositories/UserRepository";
import { WEBSITE_URL } from "@calcom/lib/constants";
Expand All @@ -16,11 +18,9 @@ import { parseRecurringEvent } from "@calcom/lib/isRecurringEvent";
import { getTranslation } from "@calcom/lib/server/i18n";
import type { PrismaClient } from "@calcom/prisma";
import type { Prisma } from "@calcom/prisma/client";
import { SchedulingType, MembershipRole } from "@calcom/prisma/enums";
import { MembershipRole, SchedulingType } from "@calcom/prisma/enums";
import { customInputSchema } from "@calcom/prisma/zod-utils";
import { OrganizationRepository } from "@calcom/features/ee/organizations/repositories/OrganizationRepository";
import { TRPCError } from "@trpc/server";
import { getOrganizationRepository } from "@calcom/features/ee/organizations/di/OrganizationRepository.container";

interface getEventTypeByIdProps {
eventTypeId: number;
Expand All @@ -29,6 +29,7 @@ interface getEventTypeByIdProps {
isTrpcCall?: boolean;
isUserOrganizationAdmin: boolean;
currentOrganizationId: number | null;
userLocale?: string | null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this optional and nullable?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can set the default locale to 'en'

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not required, comes from authenticated user

}

export type EventType = Awaited<ReturnType<typeof getEventTypeById>>;
Expand All @@ -40,6 +41,7 @@ export const getEventTypeById = async ({
prisma,
isTrpcCall = false,
isUserOrganizationAdmin,
userLocale,
}: getEventTypeByIdProps) => {
const userSelect = {
name: true,
Expand All @@ -58,7 +60,7 @@ export const getEventTypeById = async ({
eventTypeId,
isUserOrganizationAdmin,
currentOrganizationId,
prisma
prisma,
});

if (!rawEventType) {
Expand Down Expand Up @@ -90,8 +92,8 @@ export const getEventTypeById = async ({
...child,
owner: child.owner
? await userRepo.enrichUserWithItsProfile({
user: child.owner,
})
user: child.owner,
})
: null,
});
}
Expand Down Expand Up @@ -141,19 +143,19 @@ export const getEventTypeById = async ({
children: childrenWithUserProfile.flatMap((ch) =>
ch.owner !== null
? {
...ch,
owner: {
...ch.owner,
avatar: getUserAvatarUrl(ch.owner),
email: ch.owner.email,
name: ch.owner.name ?? "",
username: ch.owner.username ?? "",
membership:
restEventType.team?.members.find((tm) => tm.user.id === ch.owner?.id)?.role ||
MembershipRole.MEMBER,
},
created: true,
}
...ch,
owner: {
...ch.owner,
avatar: getUserAvatarUrl(ch.owner),
email: ch.owner.email,
name: ch.owner.name ?? "",
username: ch.owner.username ?? "",
membership:
restEventType.team?.members.find((tm) => tm.user.id === ch.owner?.id)?.role ||
MembershipRole.MEMBER,
},
created: true,
}
: []
),
};
Expand Down Expand Up @@ -187,7 +189,7 @@ export const getEventTypeById = async ({

const currentUser = eventType.users.find((u) => u.id === userId);

const t = await getTranslation(currentUser?.locale ?? "en", "common");
const t = await getTranslation(userLocale ?? currentUser?.locale ?? "en", "common");

if (!currentUser?.id && !eventType.teamId) {
throw new TRPCError({
Expand Down Expand Up @@ -224,19 +226,19 @@ export const getEventTypeById = async ({
const isOrgEventType = !!eventTypeObject.team?.parentId;
const teamMembers = eventTypeObject.team
? eventTeamMembershipsWithUserProfile
.filter((member) => member.accepted || isOrgEventType)
.map((member) => {
const user: typeof member.user & { avatar: string } = {
...member.user,
avatar: getUserAvatarUrl(member.user),
};
return {
...user,
profileId: user.profile.id,
eventTypes: user.eventTypes.map((evTy) => evTy.slug),
membership: member.role,
};
})
.filter((member) => member.accepted || isOrgEventType)
.map((member) => {
const user: typeof member.user & { avatar: string } = {
...member.user,
avatar: getUserAvatarUrl(member.user),
};
return {
...user,
profileId: user.profile.id,
eventTypes: user.eventTypes.map((evTy) => evTy.slug),
membership: member.role,
};
})
: [];

// Find the current users membership so we can check role to enable/disable deletion.
Expand Down Expand Up @@ -274,7 +276,9 @@ export async function getRawEventType({
}: Omit<getEventTypeByIdProps, "isTrpcCall">) {
const eventTypeRepo = new EventTypeRepository(prisma);
const organizationRepo = getOrganizationRepository();
const isUserInPlatformOrganization = currentOrganizationId ? !!(await organizationRepo.findById({ id: currentOrganizationId }))?.isPlatform : false;
const isUserInPlatformOrganization = currentOrganizationId
? !!(await organizationRepo.findById({ id: currentOrganizationId }))?.isPlatform
: false;

if (isUserOrganizationAdmin && currentOrganizationId && isUserInPlatformOrganization) {
// Platform Organization Admin can access any event of the organization even without being a member of the sub-teams
Expand All @@ -284,7 +288,7 @@ export async function getRawEventType({
});
}

// Regular(Non Platform) Organization member(admin/non-admin) can access any event-type they are are a member of including sub-team events and Regular Team(non-subteam) events.
// Regular(Non Platform) Organization member(admin/non-admin) can access any event-type they are are a member of including sub-team events and Regular Team(non-subteam) events.
// Remember an organization member can stay a part of Regular Team still if that team hasn't been moved to the organization yet.
return await eventTypeRepo.findById({
id: eventTypeId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@ export const getHandler = ({ ctx, input }: GetOptions) => {
prisma: ctx.prisma,
isTrpcCall: true,
isUserOrganizationAdmin: !!ctx.user?.organization?.isOrgAdmin,
userLocale: ctx.user.locale,
});
};
Loading