Files
calendar/packages/lib/event.ts
T
8ad442f2be feat: Upgrade to Next 15 (#18834)
* wip

* update layoutHOC

* wip

* remove app router related code no longer used

* remove more

* await cookies, headers, params, serachparams

* update yarn.lock

* await cookies, headers, params, serachparams more

* update yarn lock again

* downgrade next-auth to 4.22.1

* update yarn lock

* fix

* update yarn lock

* fix type checks

* update yarn lock

* await headers and cookies

* restore pages folder

* restore yarn.lock

* update yarn.lock

* await headers and cookies

* remove

* await params in API routes

* updates

* restore next.config.js

* remove i18n from next.config.js

* Fixed tests

* Fixed types

* Removed duplicate favicon.ico

* Fixing more types

* ImageResponse moved to next/og

* Fixed prisma import issues

* dynamic import for @ewsjs/xhr

* remove deasync dep

* dynamic import for @tryvital/vital-node

* fix type checks

* add back turbopack command

* Type fix

* Removed unneeded file

* fix turbopack relative path errors

* add comments

* remove unneeded code

* Fixed build errors

* await apis

* use Promise<Params> type in defaultResponderForAppDir util

* refactor scim api route

* fix type checks

* separate app-routing.config into client-config and server-config

* wip

* refactor routing forms components

* revert unneeded changes for easier review

* fix

* fix

* use CustomTrans

* fix type

* fix unit tests

* fix type error

* fix build error

* fix build error

* fix build error

* fix warnings

* fix warnings

* upgrade @tremor/react and tailwindcss

* numCols -> numItems

* fix forgot-password e2e test

* fix 1 more e2e test

* fix login e2e test

* fix e2e tests

* fix e2e tests

* clean up

* remove error

* use tremor/react 2.11.0

* fix

* rename CustomTrans to ServerTrans

* address comment

* fix test

* fix ServerTrans

* fix

* fix type

* fix ServerTrans usages 1

* fix translations

* fix type checks

* fix type checks

* link styling

* fix

---------

Co-authored-by: Keith Williams <keithwillcode@gmail.com>
Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
2025-03-20 21:30:51 -03:00

144 lines
4.7 KiB
TypeScript

import type { TFunction } from "i18next";
import z from "zod";
import { guessEventLocationType } from "@calcom/app-store/locations";
import type { Prisma } from "@calcom/prisma/client";
export const nameObjectSchema = z.object({
firstName: z.string(),
lastName: z.string().optional(),
});
function parseName(name: z.infer<typeof nameObjectSchema> | string | undefined) {
if (typeof name === "string") return name;
else if (typeof name === "object" && nameObjectSchema.parse(name))
return `${name.firstName} ${name.lastName}`.trim();
else return "Nameless";
}
export type EventNameObjectType = {
attendeeName: z.infer<typeof nameObjectSchema> | string;
eventType: string;
eventName?: string | null;
teamName?: string | null;
host: string;
location?: string | null;
eventDuration: number;
bookingFields?: Prisma.JsonObject | null;
t: TFunction;
};
export function getEventName(eventNameObj: EventNameObjectType, forAttendeeView = false) {
const attendeeName = parseName(eventNameObj.attendeeName);
if (!eventNameObj.eventName)
return eventNameObj.t("event_between_users", {
eventName: eventNameObj.eventType,
host: eventNameObj.teamName || eventNameObj.host,
attendeeName,
interpolation: {
escapeValue: false,
},
});
let eventName = eventNameObj.eventName;
let locationString = eventNameObj.location || "";
if (eventNameObj.eventName.includes("{Location}") || eventNameObj.eventName.includes("{LOCATION}")) {
const eventLocationType = guessEventLocationType(eventNameObj.location);
if (eventLocationType) {
locationString = eventLocationType.label;
}
eventName = eventName.replace("{Location}", locationString);
eventName = eventName.replace("{LOCATION}", locationString);
}
let dynamicEventName = eventName
// Need this for compatibility with older event names
.replaceAll("{Event type title}", eventNameObj.eventType)
.replaceAll("{Scheduler}", attendeeName)
.replaceAll("{Organiser}", eventNameObj.host)
.replaceAll("{Organiser first name}", eventNameObj.host.split(" ")[0])
.replaceAll("{USER}", attendeeName)
.replaceAll("{ATTENDEE}", attendeeName)
.replaceAll("{HOST}", eventNameObj.host)
.replaceAll("{HOST/ATTENDEE}", forAttendeeView ? eventNameObj.host : attendeeName)
.replaceAll("{Event duration}", `${String(eventNameObj.eventDuration)} mins`)
.replaceAll(
"{Scheduler first name}",
attendeeName === eventNameObj.t("scheduler") ? "{Scheduler first name}" : attendeeName.split(" ")[0]
);
const { bookingFields } = eventNameObj || {};
const { name } = bookingFields || {};
if (name && typeof name === "object" && !Array.isArray(name) && typeof name.lastName === "string") {
dynamicEventName = dynamicEventName.replaceAll("{Scheduler last name}", name.lastName.toString());
}
const customInputvariables = dynamicEventName.match(/\{(.+?)}/g)?.map((variable) => {
return variable.replace("{", "").replace("}", "");
});
customInputvariables?.forEach((variable) => {
if (!eventNameObj.bookingFields) return;
const bookingFieldValue = eventNameObj.bookingFields[variable as keyof typeof eventNameObj.bookingFields];
if (bookingFieldValue) {
let fieldValue;
if (typeof bookingFieldValue === "object") {
if ("value" in bookingFieldValue) {
fieldValue = bookingFieldValue.value?.toString();
} else if (variable === "name" && "firstName" in bookingFieldValue) {
const lastName = "lastName" in bookingFieldValue ? bookingFieldValue.lastName : "";
fieldValue = `${bookingFieldValue.firstName} ${lastName}`.trim();
}
} else {
fieldValue = bookingFieldValue.toString();
}
dynamicEventName = dynamicEventName.replace(`{${variable}}`, fieldValue || "");
}
});
return dynamicEventName;
}
export const validateCustomEventName = (value: string, bookingFields?: Prisma.JsonObject | null) => {
let customInputVariables: string[] = [];
if (bookingFields) {
customInputVariables = Object.keys(bookingFields).map((customInput) => {
return `{${customInput}}`;
});
}
const validVariables = customInputVariables.concat([
"{Event type title}",
"{Organiser}",
"{Scheduler}",
"{Location}",
"{Organiser first name}",
"{Scheduler first name}",
"{Scheduler last name}",
"{Event duration}",
//allowed for fallback reasons
"{LOCATION}",
"{HOST/ATTENDEE}",
"{HOST}",
"{ATTENDEE}",
"{USER}",
]);
const matches = value.match(/\{([^}]+)\}/g);
if (matches?.length) {
for (const item of matches) {
if (!validVariables.includes(item)) {
return item;
}
}
}
return true;
};